16

When running an XCT UI test it is possible to put the application under test in the background with:

XCUIDevice().pressButton(XCUIDeviceButton.Home)

It it possible in some way to bring the app back to foreground (active state) without relaunching the application?

PistolPete
  • 766
  • 3
  • 10
  • 25

6 Answers6

12

Update for Xcode 9: Starting in Xcode 9, you can now simply call activate() on any XCUIApplication.

let myApp = XCUIApplication()
myApp.activate() // bring to foreground

https://developer.apple.com/documentation/xctest/xcuiapplication/2873317-activate


Yes, it is. But, you'll need XCUIElement's private headers (which are available via header dump from Facebook here). In order to foreground the app, you need to call resolve which I believe resolves the element's query (which for applications means foregrounding the app).

For Swift, you'll have to import the XCUIElement.h into your bridging header. For Objective-C you'll just need to import XCUIElement.h.

With the app backgrounded:

Swift:

XCUIApplication().resolve()

Objective-C

[[XCUIApplication new] resolve];

If this is the only functionality you need, you could just write a quick ObjC category.

@interface XCUIElement (Tests)
- (void) resolve;
@end

If you need to launch / resolve another app. Facebook has an example of that here by going through the Springboard.

Chase Holland
  • 2,178
  • 19
  • 23
  • I'm sure this method also works, but it's a lot of workaround for what you can simply accomplish by using `XCUIApplication().launch()`. – skim Jan 19 '17 at 18:58
  • When I wrote this -- calling `launch()` killed the app and restarted it. As far as I know, you still need to call `resolve()` if you need to foreground the app without relaunching it. – Chase Holland Jan 20 '17 at 23:32
  • I think you're right. I was only thinking about launching the app (not without killing the app first). – skim Jan 20 '17 at 23:52
  • Is this still working for you guys? I feel @brandenbyers solution might just be better. – gran_profaci Apr 12 '17 at 01:41
  • How do you do this part?: you'll have to import the XCUIElement.h into your bridging header – reutsey Aug 08 '17 at 20:48
  • Yes @rfodge, you'll need to import the category file into your bridging header. As of Xcode 9, this is no longer necessary, though, as there is a new public API to accomplish this task. – Chase Holland Aug 09 '17 at 22:37
  • Yeah i saw that is what is needed to do but how exactly do you do that? Sorry i am not familiar with where i even find the bridging header, or what i would need to put. If you could let me know or add to above, that would be awesome! Thamks – reutsey Aug 10 '17 at 00:02
  • If you add any objective-c class file to your test target (via the File -> New File, not dragging in), Xcode will give you an option to create a bridging header automatically. Just replace the contents of the created file with the XCUIElement interface. – Chase Holland Aug 11 '17 at 01:09
  • The `.activate()` doesn't seem to be working in Xcode 10.2. It always fails for me. – Jay Whitsitt Apr 04 '19 at 23:11
5

As of Xcode 8.3 and iOS 10.3, you can accomplish this with Siri:

XCUIDevice.shared().press(XCUIDeviceButton.home)
XCUIDevice.shared().siriService.activate(voiceRecognitionText: "Open {appName}")

Include @available(iOS 10.3, *) at the top of your test suite file and you should be good to go!

brandenbyers
  • 486
  • 4
  • 12
  • It is important to note that to get this to work on the iOS Simulator you need to enable Siri in the Settings app (off by default). I'm not sure if there is a way to turn this on via a command line argument or anything unfortunately. – Mark Feaver May 31 '17 at 21:38
  • Good point. Siri must be enabled for this to work whether a physical device or simulator. Luckily, siri only need be enabled once on each simulated device. From my experience the setting remains between relaunches of simulator. I haven't found a way to enable siri on simulators via the command line. – brandenbyers Jun 01 '17 at 16:31
3

This is what I have in my XCUITest and it works like a charm (xcode 10.1 and test device is iPhone X 11.0)

func testWhatever() {

// You test steps go here until you need the background foreground to run

XCUIDevice.shared.press(XCUIDevice.Button.home) // To background the app XCUIApplication().activate() // To bring the app back

// You test continues after background foreground has been done. }

Jasmeet Singh
  • 325
  • 2
  • 10
1

If somebody needs just move app back from background i have written (based on answer above) category that really works(great thanks to pointing to FB git)

@implementation XCUIApplication(SpringBoard)

+ (instancetype)springBoard
{
    XCUIApplication * springboard  = [[XCUIApplication alloc] performSelector:@selector(initPrivateWithPath:bundleID:)
                                                                   withObject:nil
                                                                   withObject:@"com.apple.springboard"];




    [springboard performSelector:@selector(resolve) ];
    return springboard;
}

- (void)tapApplicationWithIdentifier:(NSString *)identifier
{
    XCUIElement *appElement = [[self descendantsMatchingType:XCUIElementTypeAny]
                           elementMatchingPredicate:[NSPredicate predicateWithFormat:@"identifier = %@", identifier]
                           ];
    [appElement tap];
}
@end
alexdmukh
  • 11
  • 1
0

For Swift, you need to declare the XCUIApplication private methods interface in Bridging-Header.h like this:

@interface XCUIApplication (Private)
- (id)initPrivateWithPath:(NSString *)path bundleID:(NSString *)bundleID;
- (void)resolve;
@end

Then call resolve() in your test cases to bring the app back:

XCUIApplication().resolve()
Alex Zavatone
  • 4,106
  • 36
  • 54
Alex Bin Zhao
  • 398
  • 3
  • 11
  • Where do you find the Brigding-Header.h file? – reutsey Aug 08 '17 at 20:31
  • 1
    It's automatically created when you add an objc file to your Swift target. If you don't already have one, the easiest thing to do is to just create an empty objc file and add it to your target. Xcode will ask if you want a bridging header -- say yes. Then delete the empty objc file you created. The bridging header will remain. Otherwise you can create a header file and set its path in `Project->Build Settings->Objective-C Bridging Header`. – Chase Holland May 22 '19 at 18:26
0

Since Xcode 13 we got several errors that the app was not in foreground state after returning to the app.

applying this code to our "goToSpringboardAndBack()" works

XCUIDevice.shared.press(XCUIDevice.Button.home)
if XCUIApplication().wait(for: .runningBackground, timeout: 5.0) {
   XCUIApplication().activate()
}

_ = XCUIApplication().wait(for: .runningForeground, timeout: 5.0)
´´´