I have a UITest (implemented using XCTest) in which I want to test my app’s behaviour when user want to disable precise location (a new feature introduced in iOS 14).
When location authorization system alert appears (check attached screenshot), I can access it with the following code
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let locationAlert = springboard.alerts.element
And I can access the precise location button with:
locationAlert.buttons["Esatta: sì"]
However, when interacting with it, the alert is considered as a "blocking" element and the default "interruption monitor" is invoked, resulting in the alert to be dismissed
t = 834.87s Check for interrupting elements affecting "Esatta: sì" Button
t = 834.92s Found 1 interrupting element:
t = 834.92s Find the "Vuoi consentire a “*****” di utilizzare la tua posizione?" Alert
t = 834.97s "Vuoi consentire a “*****” di utilizzare la tua posizione?" Alert from Application 'com.apple.springboard'
t = 834.97s Invoking UI interruption monitors for "Vuoi consentire a “*****” di utilizzare la tua posizione?" Alert from Application 'com.apple.springboard'
t = 834.97s Find the "Vuoi consentire a “*****” di utilizzare la tua posizione?" Alert
t = 835.01s Checking existence of `Button`
t = 835.06s Get all elements bound by accessibility element for: Elements matching predicate 'userTestingAttributes CONTAINS "default-button"'
t = 835.14s Checking existence of `"Non consentire" Button`
t = 835.19s Default interruption handler attempting to dismiss alert by tapping "Non consentire" Button.
t = 835.19s Tap "Non consentire" Button
t = 835.19s Wait for com.apple.springboard to idle
t = 835.22s Find the "Non consentire" Button
t = 835.28s Check for interrupting elements affecting "Non consentire" Button
t = 835.33s Synthesize event
t = 835.42s Wait for com.apple.springboard to idle
t = 835.84s Verifying handling...
t = 835.84s Check for interrupting elements affecting "Esatta: sì" Button
t = 835.85s Wait for xxxxxx to idle
t = 835.91s Confirmed successful handling of interrupting element
This only occurs when using Xcode 12.5, I don't have any problem with previous versions.
Related
My UITest requires a button to be tapped and the UIInteruptionMonitor should handle it. However, what happens is the button gets tapped, the interruption appears and gets handled and then it tries to tap the button again. It seems to think it hasn't actually tapped the button when it has...
I have the following code:
addUIInterruptionMonitor(withDescription: "Permissions") { alert -> Bool in
let okButton = alert.buttons["OK"]
if okButton.exists {
okButton.tap()
}
return true
}
app.buttons["Enable"].tap()
What happens is the following:
t = 91.24s Find the "Enable" Button
t = 91.40s Check for interrupting elements affecting "Enable" Button
t = 91.42s Wait for com.apple.springboard to idle
t = 91.85s Found 1 interrupting element:
t = 91.86s Find the "“MyApp” Would Like to Access the Camera" Alert
t = 92.03s "“MyApp” Would Like to Access the Camera" Alert from Application 'com.apple.springboard'
t = 92.03s Invoking UI interruption monitors for "“MyApp” Would Like to Access the Camera" Alert from Application 'com.apple.springboard'
t = 92.03s Invoking Permissions
t = 92.04s Checking existence of `"OK" Button`
t = 92.21s Tap "OK" Button
t = 92.21s Wait for com.apple.springboard to idle
t = 92.50s Find the "OK" Button
t = 92.66s Check for interrupting elements affecting "OK" Button
t = 92.82s Synthesize event
t = 93.14s Wait for com.apple.springboard to idle
t = 93.54s Verifying handling...
t = 93.54s Check for interrupting elements affecting "Enable" Button
t = 93.55s Wait for com.apple.springboard to idle
t = 94.10s Confirmed successful handling of interrupting element
t = 94.10s Synthesize event
t = 95.39s Scroll element to visible
t = 96.55s Failed: Failed to scroll to visible (by AX action) Button, label: 'Enable', error: Error kAXErrorCannotComplete performing AXAction 2003 on element AX element pid: 62934, elementOrHash.elementID: 140664883255456.613
t = 97.57s Retrying `Tap "Enable" Button` (attempt #2)
This is normal. When interrupted by a system alert, the intended action could not be performed, so it is retried after successful handling of the interruption.
More generally, you must check within the handler if you dealt with the interruption successfully (e.g. you found the ok button and could tap it), otherwise, you must return false.
After integrating callkit into video call app pressing the power button is ending the call when the call is in progress
Below is the provider configuration:
static var providerConfiguration: CXProviderConfiguration {
let providerConfiguration = CXProviderConfiguration(localizedName: "AppName")
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.phoneNumber]
return providerConfiguration
}
below is CXCallUpdate to report that there is an incoming call:
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: handle)
update.supportsDTMF = true;
update.hasVideo = hasVideo;
update.supportsGrouping = false;
update.supportsUngrouping = false;
update.supportsHolding = false;
If we see cisco webex video call, there also callkit has been integrated, but for video call pressing the power button is not ending the call when call is in progress. But pressing the power button is ending the call for audio call. I observed the same behaviour with WhatsApp video call as well.
This is the intended behaviour: if try to do the same thing with the iOS built-in phone app, you'll obtain the same result.
EDIT
The power button ends a call if and only if the call is running through the built-in speaker on top of the screen. In any other case (i.e. the audio is playing through headphones, bluetooth or built-in loudspeaker) the power button will not end the call.
Trying to solve a Game Center issue that seems to be happening within the GCMatchmakerViewController, but maybe I'm somehow causing it. There are two ways to match to players via the Invite Friends button: by typing their email (or selecting from contacts) or by tapping from presented list of people played with recently.
If I select via the list of people played with recently, a notification is sent to them, and if they accept, the app launches, goes to the proper screen, presents the GCMatchmakerViewController, connects, accepts, and away we go with the game.
If I select by typing/selecting their contact, an iMessage is sent, they go to Messages, tap the message bubble. If the game app is already running, it is brought to foreground, GCMatchmakerViewController is presented, connects, accepts, and away we go. But if the app is not running, it launches, goes to proper screen, presents GCMatchmakerViewController, tries connecting, and just sits there forever.
Why is the connection not happening in that latter scenario?
The screen that is displayed when it's stuck connecting is this:
The code that presents the vc that issues the invitation:
let localPlayer: GKLocalPlayer = GKLocalPlayer.local
if localPlayer.isAuthenticated {
let matchrequest = GKMatchRequest()
matchrequest.minPlayers = 2
matchrequest.maxPlayers = 4
matchrequest.defaultNumberOfPlayers = 2
matchrequest.inviteMessage = "Want to play: " + "some name"
if let mmVC = GKMatchmakerViewController(matchRequest: matchrequest) {
mmVC.matchmakerDelegate = self
self.present(mmVC, animated: true)
}
}
The code that accepts the invitation:
if let response = GKMatchmakerViewController.init(invite: invite) {
response.matchmakerDelegate = newGameVC
present(response, animated: true, completion: nil)
}
Best as I can tell, my app doesn't know which scenario is in play, and does not choose different behaviors based on that. Stumped, and not sure how to advise users. Thanks for any ideas.
private func acceptPermissionAlert() {
_ = addUIInterruptionMonitor(withDescription: "") { alert -> Bool in
if alert.buttons["Don’t Allow"].exists { //doesnt get here second time
alert.buttons.element(boundBy: 1).tapWhenExists()
return true
}
return false
}
}
and this doesn't work for:
In the beginning of the app, it works perfect while accepting permission for notifications, but here, it doesn't work. Why is this?
I'vs found that addUIInterruptionMonitor sometimes doesn't handle an alert in time, or until tests have finished. If it isn't working, try using Springboard, which manages the iOS home screen. You can access alerts, buttons, and more from there, and this is particularly useful for tests where you know exactly when an alert will show.
So, something like this:
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let alertAllowButton = springboard.buttons.element(boundBy: 1)
if alertAllowButton.waitForExistence(timeout: 5) {
alertAllowButton.tap()
}
The buttons.element(boundBy:1) will ensure you tap the button on the right, change 1 to 0 to tap the left, (because sometimes the ' in "Don't Allow" causes a problem).
Add:
app.tap()
at the end of the method.
This is because you need to interact with the app for the handler to fire.
After adding the interruption monitor, you should continue to interact with the app as if it has not appeared.
Also note that you have a 'smart quote' in your button identifier, instead of a regular apostrophe.
let photosAlertHandler = addUIInterruptionMonitor(withDescription: "Photo Permissions") { alert -> Bool in
if alert.buttons["Don't Allow"].exists {
alert.buttons.element(boundBy: 1).tapWhenExists()
return true
}
return false
}
// Do whatever you want to do after dismissing the alert
let someButton = app.buttons["someButton"]
someButton.tap() // The interruption monitor's handler will be invoked if the alert is present
When the next interaction happens after the alert appears, the interruption monitor's handler will be invoked and the alert will be handled.
You should also remove the interruption monitor when you think you're done with it, otherwise it will be invoked for any other alerts that appear.
removeUIInterruptionMonitor(photosAlertHandler)
In my application if user lock the mobile.I need to navigate to login screen.I have implemented resume event to navigate login screen if user unlock the device.can anybody tell Why Cordova resume event is not firing in power lock mode / Sleep mode in iOS
is there any other event i need to use in lock mode?
P.S It is working in minimizing the app and maximizing the app
Although in IOS a resume event is fired when minimizing or maximizing an app by pressing the home button, it appears that a resume event is not fired when "closing" or "staring" the app by pressing the power button at least in IOS.
A possible JS-solution might be to check for inactivity. Let's say when an app has not not received any events triggered by an user for some time(30 seconds and if no real pause-event has been fired since) for instance click/touch-events then it can be assumed that the app can still execute some code(so it's still in foreground) and is "paused":
// threshold for inactivity state
var idleTimeout = 30000;
// variable that holds the time in seconds, which indicates how long the app has not received certain events
var timeInSecondsPassed = 0;
// interval instance
var intervalInstance = null;
// variable to handle the transition from "pause" to "resume" state
var inPauseState = false;
function startPauseListener() {
timeInSecondsPassed = 0;
var resetPassedTime = function(){
timeInSecondsPassed = 0;
// has the app reached the "pause" state and
// currently receiving certain events -> the "resume" state is reached
if(inPauseState){
inPauseState = false;
// the "resume" state is reached here
// so the same code might be executed here as it is in the resume-listener
}
};
document.ontouchstart = resetPassedTime;
document.onclick = resetPassedTime;
document.onscroll = resetPassedTime;
document.onkeypress = resetPassedTime;
intervalInstance = setInterval(checkPauseState,1000);
}
function clearPauseListener() {
clearInterval(intervalInstance);
timeInSecondsPassed = 0;
}
function checkPauseState() {
timeInSecondsPassed += 1000;
if (timeInSecondsPassed >= idleTimeout) {
inPauseState = true;
timeInSecondsPassed = 0;
// run further code here to handle "pause" state
// at this point it is assumed as soon as the app receives click/touch-events again a "resume" state is reached.
}
}
function onDeviceReady() {
// handle android devices so that the interval is stopped when a real pause event is fired and started when a real resume event is fired
document.addEventListener("resume", function(){
startPauseListener();
// your actual code to handle real resume events
}, false);
document.addEventListener("pause", function(){
clearPauseListener();
}, false);
}
It has to be noted that when the app is really paused so a pause event is fired the code above is not run in IOS but in android so that's why you might have to handle this szenario in android differently by taking advandage of both resume and pause-Listener for in android when the app is minimized by the home button the interval would still be executed and consumes CPU in the background.
And please also note that it's only a kind of concept-code and not tested in any device!!!
Hope this helps.
There's an iOS-specific event called active that "detects when users disable the Lock button to unlock the device with the app running in the foreground".
Check the documentation at the bottom of the resume doc page:
https://cordova.apache.org/docs/en/5.1.1/cordova/events/events.resume.html