Xcode UI Tests Multiple Buttons Found - ios

I'm having a problem where my UI Test says multiple buttons found when using the following code.
app.buttons["Upgrade"].tap()
So I reran my unit test and set a breakpoint right before running that line and hit the record button and clicked the button and it generated the following code.
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).buttons["Upgrade"].tap()
Of course at the top of the test I have let app = XCUIApplication().
Any idea why this would be happening?
Sometimes when running p UIApplication.shared.windows in the debugger it has 2 values in the array. I'm not sure why as I never have multiple windows. The only interactions I have with windows is setting UIApplication.shared.keyWindow?.rootViewController to different view controllers sometimes, and the following code in didFinishLaunchingWithOptions.
// Get view controllers ready
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController: ViewController = mainStoryboard.instantiateViewController(withIdentifier: "FirstView") as! ViewController
// Show view controller
self.window?.rootViewController = mainViewController
self.window?.makeKeyAndVisible()
That is within an if statement and within the else statement I have pretty much the same code except instead of FirstView it's SecondView.

This message appears because there is more than one button on the screen with the accessibilityIdentifier, accessibilityLabel or value, "Upgrade", so it can't work out which one to tap.
The reason that it works when you use the recorded version is because the recording tool has identified that the search needs to be narrowed down to search inside the element of type .other at index 1, in order to be sure of which "Upgrade" button to interact with.
It's not a problem with your window(s), but with the uniqueness of your buttons' identifiers, and how you're handling them in your test.
In a situation where the button is only used once on the page of your app in question, it's best to set a unique accessibilityIdentifier on the UIButton. Its value should be unique within that page of your app, so make sure you aren't using the same string anywhere else. Then you can access the button unambiguously:
// app code
let upgradeButton: UIButton!
upgradeButton.accessibilityIdentifier = "upgradeButton"
// test code
let app = XCUIApplication()
let upgradeButton = app.buttons["upgradeButton"]
upgradeButton.tap()
In the situation where there are multiple instances of the same upgrade button on screen at the same time (e.g. where the button is part of a repeated pattern on the screen, like if there are lots of products for sale), it's OK for them each to have the same accessibilityIdentifier, but you should change the way you access the element in your test, using element(boundBy:) to access items at specified indices:
// app code
let upgradeButton: UIButton!
upgradeButton.accessibilityIdentifier = "upgradeButton"
// test code
let app = XCUIApplication()
let upgradeButton = app.buttons["upgradeButton"].element(boundBy: 1) // second upgrade button
upgradeButton.tap()
In this situation, you could also take the approach of finding the correct container view, and searching for the upgrade button inside it.
// test code
let app = XCUIApplication()
let upgradeButtonContainer = app.descendants(matching: .any).containing(.button, identifier: "upgradeButton").element(boundBy: 1) // second upgrade button-containing element
let upgradeButton = upgradeButtonContainer.buttons["upgradeButton"]
upgradeButton.tap()

I faced same issue and tried multiple things to get out of it. Ultimately i found out out that the issue started happening when i updated my Xcode to 8.3.3 and simulator os to iOS 10.3.
Switched my simulator os back to iOS 10.2 and it worked perfectly.

I am seeing similar problems since updating to Xcode 8.3 where the XCUIElementQuery is returning multiple results when there should be only one. There may be a bug introduced in this build.
https://forums.developer.apple.com/thread/75546
describes the bug and the radar is here:
https://openradar.appspot.com/31498932
I've tried the posted workaround by trying to coerce my queries to return the desired object as an XCUIElementQuery and then using element(boundBy:) and it worked for me.
It's upsetting how unstable the UI Tests are.

Related

How to fix/debug a VoiceOver bug?

What's my problem with the VO?
It gets stuck. It focuses only on the status bar (so it doesn't see the app at all) or it focuses temporarily on a header bar in the app (so I can change the focus between about 3 element in that field). Neither switching the VO off and on nor standard gestures can help a user in such a situation.
When exactly does it happen?
The app is quite big so the exact reason is hard to determine. That's why I want to know how you would debug it. But generally the app has login screens and inside screens. When the app starts and immediately goes to one of inside screens, the VO works perfectly. When the app starts and goes through login screens, the VO gets stuck after logging in.
Any code?
To change between the login screens and the inside screens I use UIApplicationDelegate
let storyboard = UIStoryboard(name: "StoryboardName", bundle: nil)
let vc: WantedViewController = storyboard.instantiateViewController(withIdentifier: "WantedViewControllerId") as! WantedViewController
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
Maybe it's too big change for the VO. The app uses also JGProgressHUD but I think I checked quite well that it couldn't be a reason.
Any suggestions and questions are welcome!
If you are using native UIKit widgets, then accessibility should be built in and Voiceover should find the widget. If you have a custom widget, you'll need to set isAccessibilityElement.
The presented code caused the issue indeed. I advise people with a similar problem to perform sergue instead:
performSegue(withIdentifier: "SegueToWantedViewControllerId", sender: nil)
Define it in storyboard for example with Kind as Present Modally and Presentation as Full Screen to get the same result but with VoiceOver working.

Crash when instantiating ARSCNView for the second time

I have a problem in my iPhone app when trying to instantiate an ARSCNView again, after destroying it.
In my ViewController I programmatically create an ARSCNView for motion capture interaction:
func addARSceneView() {
arSceneView = ARSCNView(frame: self.view.frame)
arSceneView.loops = true
arSceneView.session.delegate = self
self.view.addSubview(arSceneView)
arSceneView.session.run(ARBodyTrackingConfiguration())
}
When the user leaves this part of the app, I tear it down like this:
func removeARSceneView() {
arSceneView.session.pause()
arSceneView.pause(self)
arSceneView.session.delegate = nil
arSceneView.removeFromSuperview()
arSceneView = nil
}
Later, when I try to instantiate an ARSCNView for the second time using the first function above, it crashes with an EXC_BAD_ACCESS in the constructor:
I also tried to use a view from a xib which contains an ARSCNView but the same problem occurs, in that case in the init(coder) function of that view.
I found nothing on this problem, I guess usually developers only create an ARSCNView once.
TLDR: Turn "Metal API Validation" on in your scheme.
I found the culprit, after creating a sample project with only the ARSCNView, which did not have this problem. I started by stripping everything away from my original project until it was as barebones as the sample. That did not solve it, so I compared every little setting of the two, and behold: in the "Run" scheme of the original project, under "Diagnostics", I had "Metal – API Validation" ticked off. I don't remember when and why I did that; I assume it was some attempt to improve performance at one point. However, enabling this checkbox solved the problem completely.

Weird situation Crashes on Mapview in storyboard when Add child controller or Present (Sample code added)

I have view Controller in storyboard with mapview only.
class MapVC: UIViewController {
class func viewController () -> MapVC {
let storyboard = UIStoryboard(name: "Dashboard", bundle: nil)
return storyboard.instantiateViewController(withIdentifier: "MapVC") as! MapVC
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
I have simple Login screen
On Login button action I call this method
private func addChildVC (_ vc:UIViewController) {
self.removeAllChild()
self.addChild(vc)
vc.view.frame = self.view.bounds
self.view.addSubview(vc.view)
vc.didMove(toParent: self)
self.currentCenterViewController = vc
}
With
MapVC.viewController()
App Crashes on
vc.view.frame = self.view.bounds
Weird is if I am debugging like debugger attached and when I press on Login App not crashing but when Debugger not attached on launch and then I start app in simulator then attach process and when I press login button App crashes
Present view controller is also not working crashes the app
If I remove Mapview from storyboard app is working fine in both case
When I check vc.isViewLoaded is returning false
tried loadViewIfRequired()
Clean Derived Data, Clean project restart xcode.
Verify that I have added correct Identifers
I am not able to understand the reason Please help
EDIT
Sample App
https://drive.google.com/open?id=1-otaZhhhDEH4p29CgQP7xZSX9tizceq8
Steps to reproduce
1) Run in any simulator of ios 12
2) Press stop from xcode
3) From simulator launch the app
4) Press button
EDIT2
issue video https://drive.google.com/file/d/1z9C3Re_oVYkRncnE22xRoef2Kv2WyzId/view
File a bug https://bugreport.apple.com/web/?problemID=46774084
I don't know what actually issue and why your app crash. But I have found solution.
Add MapKit.framework in Link Binary With Libraries will fix you issue.
I hope this will help you.
I have found the actual reason behind it.
It is because of view debugger framework
Here is reply from apple developer team
Engineering has provided the following information regarding this issue:
The sample project provided is not configured correctly.
The application is using the MapKit framework, but has not been configured to link with MapKit. When run using the debugger, MapKit is pulled in because it’s linked by the view debugger framework.
When the app is launched from the simulator, without the view debugger, MapKit will not be loaded into the runtime as the app has not specified this as a dependency and therefor it crashes.
Updating your sample project to link MapKit solves the issue with the application crashing.

UITesting in iOS: tap() method doesn't work properly

I am facing a strange problem with UITesting in Swift under iOS 10.3.2 and Xcode 8.3.3 .
I am running a simple test inside my app which should tap and verify every single row inside a TableView (is the Settings view of my application so every single row is loaded from the disk and nothing is downloaded from the web at this step), but for a strange reason when I tap on a row which previously wasn't visible the .tap() method doesn't work.
Example Code:
let app = XCUIApplication()
let myTableView = app.tables.element(matching: .table, identifier: "tableView01")
myTableView.swipeUp(). //I swipe so that the cell becomes visible...
let myCell = myTableView.cells.element(matching: .cell, identifier: "tappableCell")
myCell.tap()
The strange thing is that I have a correct output from the console in Xcode.
t = 328.24s Tap "tappableCell" Cell
t = 328.24s Wait for app to idle
t = 328.80s Find the "tappableCell" Cell
t = 328.80s Snapshot accessibility hierarchy for com.thefloow.enterprise.ownbrand
t = 328.92s Find: Descendants matching type Table
t = 328.92s Find: Element at index 0
t = 328.92s Find: Descendants matching type Cell
t = 328.92s Find: Elements matching predicate '"tappableCell" IN identifiers'
t = 329.04s Wait for app to idle
t = 329.22s Synthesize event
t = 329.37s Wait for app to idle
Another strange thing is that if I put a breakpoint before the tap method and then I run it from the command line it works perfectly giving me the same output from the console.
What could be the reason of this strange behaviour?
Thank you for your help!
Andrea
I found that the problem is in the framework. After scrolling the UI doesn't respond properly so I had to double tap on the cell to resign the UIResponder and then the test works properly. I've also reported the issue to  to know if it is a regular behaviour or not.
However thank you to everyone! Happy coding!

Swift - Deeplink within app to launch popup viewcontroller

I'm trying to launch a deeplink from within a Label in Swift, the intention is that the deeplink launches a popup view controller with some additional information, these deeplinks will not be available externally in the app.
I have followed the following youtube video to setup the new view controller, https://www.youtube.com/watch?v=FgCIRMz_3dE, but the example here is on a button click, and i would like this to be dynamic as the content is driven from a database, i was hoping to use the following (https://github.com/TTTAttributedLabel/TTTAttributedLabel) to dyanmically add deeplinks into various words within a label.
The part i'm stuck on is how to convert the following snippet of code, into a format that can be run when the deeplink is selected, my understanding is this needs to be executed within the app delegate.
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("sbPopUpID") as! PopUpViewController
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMoveToParentViewController(self)
Can anybody help here?
please rewrite in plain english with more details, but anyway is a bit obscure why use a deep link to "show a popup view controller with some additional information".
Passing info between controllers via deep links (custom URL is the correct name..) is wrong.
iOS is not web based.
So read other stackoverflow detailed answers to get the right approach.

Resources