Is there any way to automate SFSafariViewController? I like the Xcode 7 UI test feature, but it seems it does not support SFSafariViewController automation. Some of the UI flows I am testing require a web browser so the app uses SFSafariViewController to make it safer vs a web view.

- 86,719
- 67
- 308
- 465

- 2,448
- 21
- 22
-
I'm also very interested in this. Have you found some kind of way around this? – Martin Dec 16 '15 at 16:00
-
@Martin Unfortunately nothing at this time. – Bryant Luk Dec 17 '15 at 03:33
-
It's not clear to me what you want to automate exactly. – Titouan de Bailleul Jul 06 '17 at 02:28
-
In my case I want to automate a login flow into a third-party service with a test account. – Bryant Luk Jul 06 '17 at 20:52
2 Answers
If it's similar to launching extensions (currently broken with direct interactions), try tapping the screen at the point where the element you're looking for is:
Example of tapping an action sheet which launches an extension:
func tapElementInActionSheetByPosition(element: XCUIElement!) {
let tableSize = app.tables.elementBoundByIndex(0).frame.size
let elementFrame = element.frame
// get the frame of the cancel button, because it has a real origin point
let CancelY = app.buttons["Cancel"].frame.origin.y
// 8 is the standard apple margin between views
let yCoordinate = CancelY - 8.0 - tableSize.height + elementFrame.midY
// tap the button at its screen position since tapping a button in the extension picker directly is currently broken
app.coordinateWithNormalizedOffset(CGVectorMake(elementFrame.midX / tableSize.width, yCoordinate / app.frame.size.height)).tap()
}
Note: You must tap at the XCUIApplication query layer. Tapping the element by position doesn't work.

- 2,178
- 19
- 23
For now Xcode 9.3 has support for that, but it doesn't work properly because of annoying Xcode bug.
In test you can print app.webViews.buttons.debugDescription
or app.webViews.textFields.debugDescription
, and it prints correct information, but after tap
or typeText
you have crash.
To workaround you can parse debugDescription
, extract coordinates and tap by coordinate. For text field you can insert text via "Paste" menu.
private func coordinate(forWebViewElement element: XCUIElement) -> XCUICoordinate? {
// parse description to find its frame
let descr = element.firstMatch.debugDescription
guard let rangeOpen = descr.range(of: "{{", options: [.backwards]),
let rangeClose = descr.range(of: "}}", options: [.backwards]) else {
return nil
}
let frameStr = String(descr[rangeOpen.lowerBound..<rangeClose.upperBound])
let rect = CGRectFromString(frameStr)
// get the center of rect
let center = CGVector(dx: rect.midX, dy: rect.midY)
let coordinate = XCUIApplication().coordinate(withNormalizedOffset: .zero).withOffset(center)
return coordinate
}
func tap(onWebViewElement element: XCUIElement) {
// xcode has bug, so we cannot directly access webViews XCUIElements
// as workaround we can check debugDesciption, find frame and tap by coordinate
let coord = coordinate(forWebViewElement: element)
coord?.tap()
}
Article about that: https://medium.com/@pilot34/work-with-sfsafariviewcontroller-or-wkwebview-in-xcode-ui-tests-8b14fd281a1f
Full code is here: https://gist.github.com/pilot34/09d692f74d4052670f3bae77dd745889

- 885
- 11
- 15