FBSessionCommands.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "FBSessionCommands.h"
  10. #import "FBCapabilities.h"
  11. #import "FBConfiguration.h"
  12. #import "FBExceptions.h"
  13. #import "FBLogger.h"
  14. #import "FBProtocolHelpers.h"
  15. #import "FBRouteRequest.h"
  16. #import "FBSession.h"
  17. #import "FBSettings.h"
  18. #import "FBRuntimeUtils.h"
  19. #import "FBActiveAppDetectionPoint.h"
  20. #import "FBXCodeCompatibility.h"
  21. #import "XCUIApplication+FBHelpers.h"
  22. #import "XCUIApplication+FBQuiescence.h"
  23. #import "XCUIDevice.h"
  24. #import "XCUIDevice+FBHealthCheck.h"
  25. #import "XCUIDevice+FBHelpers.h"
  26. #import "XCUIApplicationProcessDelay.h"
  27. @implementation FBSessionCommands
  28. #pragma mark - <FBCommandHandler>
  29. + (NSArray *)routes
  30. {
  31. return
  32. @[
  33. [[FBRoute POST:@"/url"] respondWithTarget:self action:@selector(handleOpenURL:)],
  34. [[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)],
  35. [[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)],
  36. [[FBRoute POST:@"/wda/apps/activate"] respondWithTarget:self action:@selector(handleSessionAppActivate:)],
  37. [[FBRoute POST:@"/wda/apps/terminate"] respondWithTarget:self action:@selector(handleSessionAppTerminate:)],
  38. [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)],
  39. [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)],
  40. [[FBRoute GET:@""] respondWithTarget:self action:@selector(handleGetActiveSession:)],
  41. [[FBRoute DELETE:@""] respondWithTarget:self action:@selector(handleDeleteSession:)],
  42. [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)],
  43. // Health check might modify simulator state so it should only be called in-between testing sessions
  44. [[FBRoute GET:@"/wda/healthcheck"].withoutSession respondWithTarget:self action:@selector(handleGetHealthCheck:)],
  45. // Settings endpoints
  46. [[FBRoute GET:@"/appium/settings"] respondWithTarget:self action:@selector(handleGetSettings:)],
  47. [[FBRoute POST:@"/appium/settings"] respondWithTarget:self action:@selector(handleSetSettings:)],
  48. ];
  49. }
  50. #pragma mark - Commands
  51. + (id<FBResponsePayload>)handleOpenURL:(FBRouteRequest *)request
  52. {
  53. NSString *urlString = request.arguments[@"url"];
  54. if (!urlString) {
  55. return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:@"URL is required" traceback:nil]);
  56. }
  57. NSString* bundleId = request.arguments[@"bundleId"];
  58. NSError *error;
  59. if (nil == bundleId) {
  60. if (![XCUIDevice.sharedDevice fb_openUrl:urlString error:&error]) {
  61. return FBResponseWithUnknownError(error);
  62. }
  63. } else {
  64. if (![XCUIDevice.sharedDevice fb_openUrl:urlString withApplication:bundleId error:&error]) {
  65. return FBResponseWithUnknownError(error);
  66. }
  67. }
  68. return FBResponseWithOK();
  69. }
  70. + (id<FBResponsePayload>)handleCreateSession:(FBRouteRequest *)request
  71. {
  72. if (nil != FBSession.activeSession) {
  73. [FBSession.activeSession kill];
  74. }
  75. NSDictionary<NSString *, id> *capabilities;
  76. NSError *error;
  77. if (![request.arguments[@"capabilities"] isKindOfClass:NSDictionary.class]) {
  78. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:@"'capabilities' is mandatory to create a new session"
  79. traceback:nil]);
  80. }
  81. if (nil == (capabilities = FBParseCapabilities((NSDictionary *)request.arguments[@"capabilities"], &error))) {
  82. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:error.localizedDescription traceback:nil]);
  83. }
  84. [FBConfiguration resetSessionSettings];
  85. [FBConfiguration setShouldUseTestManagerForVisibilityDetection:[capabilities[FB_CAP_USE_TEST_MANAGER_FOR_VISIBLITY_DETECTION] boolValue]];
  86. if (capabilities[FB_SETTING_USE_COMPACT_RESPONSES]) {
  87. [FBConfiguration setShouldUseCompactResponses:[capabilities[FB_SETTING_USE_COMPACT_RESPONSES] boolValue]];
  88. }
  89. NSString *elementResponseAttributes = capabilities[FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES];
  90. if (elementResponseAttributes) {
  91. [FBConfiguration setElementResponseAttributes:elementResponseAttributes];
  92. }
  93. if (capabilities[FB_CAP_MAX_TYPING_FREQUENCY]) {
  94. [FBConfiguration setMaxTypingFrequency:[capabilities[FB_CAP_MAX_TYPING_FREQUENCY] unsignedIntegerValue]];
  95. }
  96. if (capabilities[FB_CAP_USE_SINGLETON_TEST_MANAGER]) {
  97. [FBConfiguration setShouldUseSingletonTestManager:[capabilities[FB_CAP_USE_SINGLETON_TEST_MANAGER] boolValue]];
  98. }
  99. if (capabilities[FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS]) {
  100. if ([capabilities[FB_CAP_DISABLE_AUTOMATIC_SCREENSHOTS] boolValue]) {
  101. [FBConfiguration disableScreenshots];
  102. } else {
  103. [FBConfiguration enableScreenshots];
  104. }
  105. }
  106. if (capabilities[FB_CAP_SHOULD_TERMINATE_APP]) {
  107. [FBConfiguration setShouldTerminateApp:[capabilities[FB_CAP_SHOULD_TERMINATE_APP] boolValue]];
  108. }
  109. NSNumber *delay = capabilities[FB_CAP_EVENT_LOOP_IDLE_DELAY_SEC];
  110. if ([delay doubleValue] > 0.0) {
  111. [XCUIApplicationProcessDelay setEventLoopHasIdledDelay:[delay doubleValue]];
  112. } else {
  113. [XCUIApplicationProcessDelay disableEventLoopDelay];
  114. }
  115. if (nil != capabilities[FB_SETTING_WAIT_FOR_IDLE_TIMEOUT]) {
  116. FBConfiguration.waitForIdleTimeout = [capabilities[FB_SETTING_WAIT_FOR_IDLE_TIMEOUT] doubleValue];
  117. }
  118. if (nil == capabilities[FB_CAP_FORCE_SIMULATOR_SOFTWARE_KEYBOARD_PRESENCE] ||
  119. [capabilities[FB_CAP_FORCE_SIMULATOR_SOFTWARE_KEYBOARD_PRESENCE] boolValue]) {
  120. [FBConfiguration forceSimulatorSoftwareKeyboardPresence];
  121. }
  122. NSString *bundleID = capabilities[FB_CAP_BUNDLE_ID];
  123. NSString *initialUrl = capabilities[FB_CAP_INITIAL_URL];
  124. XCUIApplication *app = nil;
  125. if (bundleID != nil) {
  126. app = [[XCUIApplication alloc] initWithBundleIdentifier:bundleID];
  127. BOOL forceAppLaunch = YES;
  128. if (nil != capabilities[FB_CAP_FORCE_APP_LAUNCH]) {
  129. forceAppLaunch = [capabilities[FB_CAP_FORCE_APP_LAUNCH] boolValue];
  130. }
  131. XCUIApplicationState appState = app.state;
  132. BOOL isAppRunning = appState >= XCUIApplicationStateRunningBackground;
  133. if (!isAppRunning || (isAppRunning && forceAppLaunch)) {
  134. app.fb_shouldWaitForQuiescence = nil == capabilities[FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE]
  135. || [capabilities[FB_CAP_SHOULD_WAIT_FOR_QUIESCENCE] boolValue];
  136. app.launchArguments = (NSArray<NSString *> *)capabilities[FB_CAP_ARGUMENTS] ?: @[];
  137. app.launchEnvironment = (NSDictionary <NSString *, NSString *> *)capabilities[FB_CAP_ENVIRNOMENT] ?: @{};
  138. if (nil != initialUrl) {
  139. if (app.running) {
  140. [app terminate];
  141. }
  142. NSError *openError;
  143. if (![XCUIDevice.sharedDevice fb_openUrl:initialUrl
  144. withApplication:bundleID
  145. error:&openError]) {
  146. NSString *errorMsg = [NSString stringWithFormat:@"Cannot open the URL %@ with the %@ application. Original error: %@",
  147. initialUrl, bundleID, openError.localizedDescription];
  148. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
  149. }
  150. } else {
  151. @try {
  152. [app launch];
  153. } @catch (NSException *e) {
  154. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:e.reason traceback:nil]);
  155. }
  156. }
  157. if (!app.running) {
  158. NSString *errorMsg = [NSString stringWithFormat:@"Cannot launch %@ application. Make sure the correct bundle identifier has been provided in capabilities and check the device log for possible crash report occurrences", bundleID];
  159. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg
  160. traceback:nil]);
  161. }
  162. } else if (appState == XCUIApplicationStateRunningBackground && !forceAppLaunch) {
  163. if (nil != initialUrl) {
  164. NSError *openError;
  165. if (![XCUIDevice.sharedDevice fb_openUrl:initialUrl
  166. withApplication:bundleID
  167. error:&openError]) {
  168. NSString *errorMsg = [NSString stringWithFormat:@"Cannot open the URL %@ with the %@ application. Original error: %@",
  169. initialUrl, bundleID, openError.localizedDescription];
  170. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
  171. }
  172. } else {
  173. [app activate];
  174. }
  175. }
  176. }
  177. if (nil != initialUrl && nil == bundleID) {
  178. NSError *openError;
  179. if (![XCUIDevice.sharedDevice fb_openUrl:initialUrl error:&openError]) {
  180. NSString *errorMsg = [NSString stringWithFormat:@"Cannot open the URL %@. Original error: %@",
  181. initialUrl, openError.localizedDescription];
  182. return FBResponseWithStatus([FBCommandStatus sessionNotCreatedError:errorMsg traceback:nil]);
  183. }
  184. }
  185. if (capabilities[FB_SETTING_DEFAULT_ALERT_ACTION]) {
  186. [FBSession initWithApplication:app
  187. defaultAlertAction:(id)capabilities[FB_SETTING_DEFAULT_ALERT_ACTION]];
  188. } else {
  189. [FBSession initWithApplication:app];
  190. }
  191. if (nil != capabilities[FB_CAP_USE_NATIVE_CACHING_STRATEGY]) {
  192. FBSession.activeSession.useNativeCachingStrategy = [capabilities[FB_CAP_USE_NATIVE_CACHING_STRATEGY] boolValue];
  193. }
  194. return FBResponseWithObject(FBSessionCommands.sessionInformation);
  195. }
  196. + (id<FBResponsePayload>)handleSessionAppLaunch:(FBRouteRequest *)request
  197. {
  198. [request.session launchApplicationWithBundleId:(id)request.arguments[@"bundleId"]
  199. shouldWaitForQuiescence:request.arguments[@"shouldWaitForQuiescence"]
  200. arguments:request.arguments[@"arguments"]
  201. environment:request.arguments[@"environment"]];
  202. return FBResponseWithOK();
  203. }
  204. + (id<FBResponsePayload>)handleSessionAppActivate:(FBRouteRequest *)request
  205. {
  206. [request.session activateApplicationWithBundleId:(id)request.arguments[@"bundleId"]];
  207. return FBResponseWithOK();
  208. }
  209. + (id<FBResponsePayload>)handleSessionAppTerminate:(FBRouteRequest *)request
  210. {
  211. BOOL result = [request.session terminateApplicationWithBundleId:(id)request.arguments[@"bundleId"]];
  212. return FBResponseWithObject(@(result));
  213. }
  214. + (id<FBResponsePayload>)handleSessionAppState:(FBRouteRequest *)request
  215. {
  216. NSUInteger state = [request.session applicationStateWithBundleId:(id)request.arguments[@"bundleId"]];
  217. return FBResponseWithObject(@(state));
  218. }
  219. + (id<FBResponsePayload>)handleGetActiveAppsList:(FBRouteRequest *)request
  220. {
  221. return FBResponseWithObject([XCUIApplication fb_activeAppsInfo]);
  222. }
  223. + (id<FBResponsePayload>)handleGetActiveSession:(FBRouteRequest *)request
  224. {
  225. return FBResponseWithObject(FBSessionCommands.sessionInformation);
  226. }
  227. + (id<FBResponsePayload>)handleDeleteSession:(FBRouteRequest *)request
  228. {
  229. [request.session kill];
  230. return FBResponseWithOK();
  231. }
  232. + (id<FBResponsePayload>)handleGetStatus:(FBRouteRequest *)request
  233. {
  234. // For updatedWDABundleId capability by Appium
  235. NSString *productBundleIdentifier = @"com.facebook.WebDriverAgentRunner";
  236. NSString *envproductBundleIdentifier = NSProcessInfo.processInfo.environment[@"WDA_PRODUCT_BUNDLE_IDENTIFIER"];
  237. if (envproductBundleIdentifier && [envproductBundleIdentifier length] != 0) {
  238. productBundleIdentifier = NSProcessInfo.processInfo.environment[@"WDA_PRODUCT_BUNDLE_IDENTIFIER"];
  239. }
  240. NSMutableDictionary *buildInfo = [NSMutableDictionary dictionaryWithDictionary:@{
  241. @"time" : [self.class buildTimestamp],
  242. @"productBundleIdentifier" : productBundleIdentifier,
  243. }];
  244. NSString *upgradeTimestamp = NSProcessInfo.processInfo.environment[@"UPGRADE_TIMESTAMP"];
  245. if (nil != upgradeTimestamp && upgradeTimestamp.length > 0) {
  246. [buildInfo setObject:upgradeTimestamp forKey:@"upgradedAt"];
  247. }
  248. return FBResponseWithObject(
  249. @{
  250. @"ready" : @YES,
  251. @"message" : @"WebDriverAgent is ready to accept commands",
  252. @"state" : @"success",
  253. @"os" :
  254. @{
  255. @"name" : [[UIDevice currentDevice] systemName],
  256. @"version" : [[UIDevice currentDevice] systemVersion],
  257. @"sdkVersion": FBSDKVersion() ?: @"unknown",
  258. @"testmanagerdVersion": @(FBTestmanagerdVersion()),
  259. },
  260. @"ios" :
  261. @{
  262. #if TARGET_OS_SIMULATOR
  263. @"simulatorVersion" : [[UIDevice currentDevice] systemVersion],
  264. #endif
  265. @"ip" : [XCUIDevice sharedDevice].fb_wifiIPAddress ?: [NSNull null]
  266. },
  267. @"build" : buildInfo.copy,
  268. @"device": [self.class deviceNameByUserInterfaceIdiom:[UIDevice currentDevice].userInterfaceIdiom]
  269. }
  270. );
  271. }
  272. + (id<FBResponsePayload>)handleGetHealthCheck:(FBRouteRequest *)request
  273. {
  274. if (![[XCUIDevice sharedDevice] fb_healthCheckWithApplication:[XCUIApplication fb_activeApplication]]) {
  275. return FBResponseWithUnknownErrorFormat(@"Health check failed");
  276. }
  277. return FBResponseWithOK();
  278. }
  279. + (id<FBResponsePayload>)handleGetSettings:(FBRouteRequest *)request
  280. {
  281. return FBResponseWithObject(
  282. @{
  283. FB_SETTING_USE_COMPACT_RESPONSES: @([FBConfiguration shouldUseCompactResponses]),
  284. FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES: [FBConfiguration elementResponseAttributes],
  285. FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY: @([FBConfiguration mjpegServerScreenshotQuality]),
  286. FB_SETTING_MJPEG_SERVER_FRAMERATE: @([FBConfiguration mjpegServerFramerate]),
  287. FB_SETTING_MJPEG_SCALING_FACTOR: @([FBConfiguration mjpegScalingFactor]),
  288. FB_SETTING_MJPEG_FIX_ORIENTATION: @([FBConfiguration mjpegShouldFixOrientation]),
  289. FB_SETTING_SCREENSHOT_QUALITY: @([FBConfiguration screenshotQuality]),
  290. FB_SETTING_KEYBOARD_AUTOCORRECTION: @([FBConfiguration keyboardAutocorrection]),
  291. FB_SETTING_KEYBOARD_PREDICTION: @([FBConfiguration keyboardPrediction]),
  292. FB_SETTING_CUSTOM_SNAPSHOT_TIMEOUT: @([FBConfiguration customSnapshotTimeout]),
  293. FB_SETTING_SNAPSHOT_MAX_DEPTH: @([FBConfiguration snapshotMaxDepth]),
  294. FB_SETTING_USE_FIRST_MATCH: @([FBConfiguration useFirstMatch]),
  295. FB_SETTING_WAIT_FOR_IDLE_TIMEOUT: @([FBConfiguration waitForIdleTimeout]),
  296. FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT: @([FBConfiguration animationCoolOffTimeout]),
  297. FB_SETTING_BOUND_ELEMENTS_BY_INDEX: @([FBConfiguration boundElementsByIndex]),
  298. FB_SETTING_REDUCE_MOTION: @([FBConfiguration reduceMotionEnabled]),
  299. FB_SETTING_DEFAULT_ACTIVE_APPLICATION: request.session.defaultActiveApplication,
  300. FB_SETTING_ACTIVE_APP_DETECTION_POINT: FBActiveAppDetectionPoint.sharedInstance.stringCoordinates,
  301. FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS: @([FBConfiguration includeNonModalElements]),
  302. FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR: FBConfiguration.acceptAlertButtonSelector,
  303. FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR: FBConfiguration.dismissAlertButtonSelector,
  304. FB_SETTING_DEFAULT_ALERT_ACTION: request.session.defaultAlertAction ?: @"",
  305. #if !TARGET_OS_TV
  306. FB_SETTING_SCREENSHOT_ORIENTATION: [FBConfiguration humanReadableScreenshotOrientation],
  307. #endif
  308. }
  309. );
  310. }
  311. // TODO if we get lots more settings, handling them with a series of if-statements will be unwieldy
  312. // and this should be refactored
  313. + (id<FBResponsePayload>)handleSetSettings:(FBRouteRequest *)request
  314. {
  315. NSDictionary* settings = request.arguments[@"settings"];
  316. if (nil != [settings objectForKey:FB_SETTING_USE_COMPACT_RESPONSES]) {
  317. [FBConfiguration setShouldUseCompactResponses:[[settings objectForKey:FB_SETTING_USE_COMPACT_RESPONSES] boolValue]];
  318. }
  319. if (nil != [settings objectForKey:FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES]) {
  320. [FBConfiguration setElementResponseAttributes:(NSString *)[settings objectForKey:FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES]];
  321. }
  322. if (nil != [settings objectForKey:FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY]) {
  323. [FBConfiguration setMjpegServerScreenshotQuality:[[settings objectForKey:FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY] unsignedIntegerValue]];
  324. }
  325. if (nil != [settings objectForKey:FB_SETTING_MJPEG_SERVER_FRAMERATE]) {
  326. [FBConfiguration setMjpegServerFramerate:[[settings objectForKey:FB_SETTING_MJPEG_SERVER_FRAMERATE] unsignedIntegerValue]];
  327. }
  328. if (nil != [settings objectForKey:FB_SETTING_SCREENSHOT_QUALITY]) {
  329. [FBConfiguration setScreenshotQuality:[[settings objectForKey:FB_SETTING_SCREENSHOT_QUALITY] unsignedIntegerValue]];
  330. }
  331. if (nil != [settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR]) {
  332. [FBConfiguration setMjpegScalingFactor:[[settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR] unsignedIntegerValue]];
  333. }
  334. if (nil != [settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION]) {
  335. [FBConfiguration setMjpegShouldFixOrientation:[[settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION] boolValue]];
  336. }
  337. if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION]) {
  338. [FBConfiguration setKeyboardAutocorrection:[[settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION] boolValue]];
  339. }
  340. if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_PREDICTION]) {
  341. [FBConfiguration setKeyboardPrediction:[[settings objectForKey:FB_SETTING_KEYBOARD_PREDICTION] boolValue]];
  342. }
  343. // SNAPSHOT_TIMEOUT setting is deprecated. Please use CUSTOM_SNAPSHOT_TIMEOUT instead
  344. if (nil != [settings objectForKey:FB_SETTING_SNAPSHOT_TIMEOUT]) {
  345. [FBConfiguration setCustomSnapshotTimeout:[[settings objectForKey:FB_SETTING_SNAPSHOT_TIMEOUT] doubleValue]];
  346. }
  347. if (nil != [settings objectForKey:FB_SETTING_CUSTOM_SNAPSHOT_TIMEOUT]) {
  348. [FBConfiguration setCustomSnapshotTimeout:[[settings objectForKey:FB_SETTING_CUSTOM_SNAPSHOT_TIMEOUT] doubleValue]];
  349. }
  350. if (nil != [settings objectForKey:FB_SETTING_SNAPSHOT_MAX_DEPTH]) {
  351. [FBConfiguration setSnapshotMaxDepth:[[settings objectForKey:FB_SETTING_SNAPSHOT_MAX_DEPTH] intValue]];
  352. }
  353. if (nil != [settings objectForKey:FB_SETTING_USE_FIRST_MATCH]) {
  354. [FBConfiguration setUseFirstMatch:[[settings objectForKey:FB_SETTING_USE_FIRST_MATCH] boolValue]];
  355. }
  356. if (nil != [settings objectForKey:FB_SETTING_BOUND_ELEMENTS_BY_INDEX]) {
  357. [FBConfiguration setBoundElementsByIndex:[[settings objectForKey:FB_SETTING_BOUND_ELEMENTS_BY_INDEX] boolValue]];
  358. }
  359. if (nil != [settings objectForKey:FB_SETTING_REDUCE_MOTION]) {
  360. [FBConfiguration setReduceMotionEnabled:[[settings objectForKey:FB_SETTING_REDUCE_MOTION] boolValue]];
  361. }
  362. if (nil != [settings objectForKey:FB_SETTING_DEFAULT_ACTIVE_APPLICATION]) {
  363. request.session.defaultActiveApplication = (NSString *)[settings objectForKey:FB_SETTING_DEFAULT_ACTIVE_APPLICATION];
  364. }
  365. if (nil != [settings objectForKey:FB_SETTING_ACTIVE_APP_DETECTION_POINT]) {
  366. NSError *error;
  367. if (![FBActiveAppDetectionPoint.sharedInstance setCoordinatesWithString:(NSString *)[settings objectForKey:FB_SETTING_ACTIVE_APP_DETECTION_POINT]
  368. error:&error]) {
  369. return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.localizedDescription
  370. traceback:nil]);
  371. }
  372. }
  373. if (nil != [settings objectForKey:FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS]) {
  374. if ([XCUIElement fb_supportsNonModalElementsInclusion]) {
  375. [FBConfiguration setIncludeNonModalElements:[[settings objectForKey:FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS] boolValue]];
  376. } else {
  377. [FBLogger logFmt:@"'%@' settings value cannot be assigned, because non modal elements inclusion is not supported by the current iOS SDK", FB_SETTING_INCLUDE_NON_MODAL_ELEMENTS];
  378. }
  379. }
  380. if (nil != [settings objectForKey:FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR]) {
  381. [FBConfiguration setAcceptAlertButtonSelector:(NSString *)[settings objectForKey:FB_SETTING_ACCEPT_ALERT_BUTTON_SELECTOR]];
  382. }
  383. if (nil != [settings objectForKey:FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR]) {
  384. [FBConfiguration setDismissAlertButtonSelector:(NSString *)[settings objectForKey:FB_SETTING_DISMISS_ALERT_BUTTON_SELECTOR]];
  385. }
  386. if (nil != [settings objectForKey:FB_SETTING_WAIT_FOR_IDLE_TIMEOUT]) {
  387. [FBConfiguration setWaitForIdleTimeout:[[settings objectForKey:FB_SETTING_WAIT_FOR_IDLE_TIMEOUT] doubleValue]];
  388. }
  389. if (nil != [settings objectForKey:FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT]) {
  390. [FBConfiguration setAnimationCoolOffTimeout:[[settings objectForKey:FB_SETTING_ANIMATION_COOL_OFF_TIMEOUT] doubleValue]];
  391. }
  392. if ([[settings objectForKey:FB_SETTING_DEFAULT_ALERT_ACTION] isKindOfClass:NSString.class]) {
  393. request.session.defaultAlertAction = [settings[FB_SETTING_DEFAULT_ALERT_ACTION] lowercaseString];
  394. }
  395. #if !TARGET_OS_TV
  396. if (nil != [settings objectForKey:FB_SETTING_SCREENSHOT_ORIENTATION]) {
  397. NSError *error;
  398. if (![FBConfiguration setScreenshotOrientation:(NSString *)[settings objectForKey:FB_SETTING_SCREENSHOT_ORIENTATION]
  399. error:&error]) {
  400. return FBResponseWithStatus([FBCommandStatus invalidArgumentErrorWithMessage:error.localizedDescription
  401. traceback:nil]);
  402. }
  403. }
  404. #endif
  405. return [self handleGetSettings:request];
  406. }
  407. #pragma mark - Helpers
  408. + (NSString *)buildTimestamp
  409. {
  410. return [NSString stringWithFormat:@"%@ %@",
  411. [NSString stringWithUTF8String:__DATE__],
  412. [NSString stringWithUTF8String:__TIME__]
  413. ];
  414. }
  415. /**
  416. Return current session information.
  417. This response does not have any active application information.
  418. */
  419. + (NSDictionary *)sessionInformation
  420. {
  421. return
  422. @{
  423. @"sessionId" : [FBSession activeSession].identifier ?: NSNull.null,
  424. @"capabilities" : FBSessionCommands.currentCapabilities
  425. };
  426. }
  427. /*
  428. Return the device kind as lower case
  429. */
  430. + (NSString *)deviceNameByUserInterfaceIdiom:(UIUserInterfaceIdiom) userInterfaceIdiom
  431. {
  432. if (userInterfaceIdiom == UIUserInterfaceIdiomPad) {
  433. return @"ipad";
  434. } else if (userInterfaceIdiom == UIUserInterfaceIdiomTV) {
  435. return @"apple tv";
  436. } else if (userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
  437. return @"iphone";
  438. }
  439. // CarPlay, Mac, Vision UI or unknown are possible
  440. return @"Unknown";
  441. }
  442. + (NSDictionary *)currentCapabilities
  443. {
  444. return
  445. @{
  446. @"device": [self.class deviceNameByUserInterfaceIdiom:[UIDevice currentDevice].userInterfaceIdiom],
  447. @"sdkVersion": [[UIDevice currentDevice] systemVersion]
  448. };
  449. }
  450. @end