Timer.periodic is not cancelled when the condition is true - flutter

I"m using a method that checks if the user's email is verified every 5 seconds, the checkIsEmailVerified() is asynchronous since it needs to send a request to check if the user has verified his email.
void checkEveryDurationUserVerification() {
Timer.periodic(
Duration(seconds: 5),
(timer) async {
if (isUserVerified) {
timer.cancel();
}
print("another check");
await checkIsEmailVerified();
},
);
}
so here is what happens, when this method is executing, it shows "another check" every 5 seconds as I want, and so the checkIsEmailVerified().
so what I have ? is a fully working email verification checker every 5 seconds, and the email is not verified yet.
when I open my email inbox and verify my email, In the app it works the listener I gave to the isUserVerified is working because it's true, and it navigates me to the home page as I want, but the Timer is not canceled even the isUserVerified is true
The "another check" is still printing in the debug console, even if the isUserVerified is true and so it should cancel that Timer**
what I think:
that this behavior is related to making the Timer's callback asynchronous
what I expect:
I want the when the isUserVerified is true the Timer to be canceled, so it stopped working after verifying the user

Related

Cancel execution of method after it starts in Flutter

Consider this method:
Future<void> methodWithAlotOfSteps()async{
callMethodA();
await callMethodB();
...
return ...;
}
which makes some computation. Suppose I want the user to be able to stop this process at any point in time (when he taps cancel button for example).
How can I stop the execution of the above method no matter what line in the method the "program counter" has reached when the user presses cancel.
I am looking for something like methodWithAlotOfSteps.cancel();.
I tried using CancelableCompleter, but even though the Future is cancelled and onCancel method of the completer is called, but the function continues execution.
I know I can set a boolean flag and check it after each "step" ("line", "call to a method"),such as :
Future<void> methodWithAlotOfSteps()async{
if(!completer.isCancelled)
callMethodA();
if(!completer.isCancelled)
await callMethodB();
...
return ...;
}
but is there a better way of doing this?
As #Jamesdlin suggested, the apparent way is to check the cancelable completer's state after each async gap:
class MyService{
CancelableCompleter completer = CancelableCompleter();
Future<void> doSomething() async {
doSomeSyncWork(); // Sync work
doAnotherSyncWork(); // Sync work
await doSomeAsyncWork(); // Async work, this will return control to event loop and make it possible to, for example, press a button to cancel the future.
// here we know we have lost control for a while, so we must check if we have been cancelled
if (completer.isCompleted) {
return;
}
doSomeMoreSyncWork();
await doSomeMoreAsyncWork();
// here we know we have lost control for a while, so we must check if we have been cancelled
if (completer.isCompleted) {
return;
}
...
completer.complete();
}
}

XCUITest verify that ui interruption handler occurred

I'm very new to swift and xcuitest. I recently came across addUIInterruptionMonitor for handling alerts that can pop up. What I would like to know is how I can verify that the alert happened and that the handler was dealt with. Take the following for example
addUIInterruptionMonitorWithDescription("Location Services") {
(alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Request Location"].tap()
app.tap() // need to interact with the app again for the handler to fire
// after this if the handler never gets called I want the test to fail
I would like to test that the alert actually happens, but as far as I understand, after my last tap() if the alert never gets triggered my handler wont be called. I need to test that the alert actually happened and then maybe add some assertions to the contents of the handler
I seem to have answered my own question on further investigation. For asynchronous testing with xcuitest I can use a XCTestExpectation which will when created, cause the test to wait until the expectation is fulfilled, or fail after a certain timeout. With that my above code becomes:
let expectation = XCTestExpectation(description: "Location Service Alert")
let locationMonitorToken = addUIInterruptionMonitorWithDescription("Location Services") {
(alert) -> Bool in
alert.buttons["Allow"].tap()
expectation.fulfill() // test waits for this before passing
return true
}
app.buttons["Request Location"].tap()
app.tap() // alert triggered here
wait(for: [expectation], timeout: 3.0)
removeUIInterruptionMonitor(locationMonitorToken)
Update: forgot to put in the wait(for: [expectation], timeout: 3.0) after alert triggered to ensure handler is called.

Automate validation of result based on auto clicking a button

There is a button on a webpage, If I click that button first time, it takes some time to get the status in progress. once it is in progress then If I click after sometime say 3 minutes then the status is coming as successful.
The problem here is I have to give sleep time between two clicks to verify the status and sometimes because of this sleep time, a status not in sync. For example I click a button and status in progress and if I click after 3 minutes, sometimes status is successful or sometimes it remains in progress which is failing my TC.
isn't there any way that button should be clicked automatically until status comes in progress and then I can verify the status. same for successful status as well.
I am using sleep between two clicks
browser.sleep(25000)
button.click();
expect(inprogress_class.getText()).toContain('in progress');
browser.sleep(100000) // waiting for a defined time to click a buttonassuming that status will be successful
button.click();
expect(successful_class.getText()).toContain('successful);
Expected: Button keeps on getting clicked until status changes
Actual: giving sleep time between click and waiting for status to change
Try the below option
To click for the first time and wait for InProgress State
await button.click();
await browser.wait(ExpectedConditions.textToBePresentInElement(await inprogress_class), 25000, 'The wait for inProgress state');
To click the button until we get the success try the below one
while (await successful_class.getText() !== 'successful') {
await button.click();
await browser.sleep(10000); // update the time with how often the button needs to be clicked
}
expect(await successful_class.getText()).toContain('successful);
Hope it helps you
If you can use async/await then it would be more easier otherwise you will have to use Promises chaining.
try with async/await approach :
await browser.wait(async () => {
await button.click();
var buttonText = await inprogress_class.getText();
return buttonText === 'in progress';
}, 180000);
expect(inprogress_class.getText()).toContain('in progress');
await browser.wait(async () => {
await button.click();
var buttonText = await successful_class.getText();
return buttonText === 'successful';
}, 180000);
expect(successful_class.getText()).toContain('successful);

Ensure processing of a REST call in flutter app in background

I need to ensure that a certain HTTP request was send successfully. Therefore, I'm wondering if a simple way exists to move such a request into a background service task.
The background of my question is the following:
We're developing a survey application using flutter. Unfortunately, the app is intended to be used in an environment where no mobile internet connection can be guaranteed. Therefore, I’m not able to simply post the result of the survey one time but I have to retry it if it fails due to network problems. My current code looks like the following. The problem with my current solution is that it only works while the app is active all the time. If the user minimizes or closes the app, the data I want to upload is lost.
Therefore, I’m looking for a solution to wrap the upload process in a background service task so that it will be processed even when the user closes the app. I found several posts and plugins (namely https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124 and https://pub.dartlang.org/packages/background_fetch) but they don’t help in my particular use case. The first describes a way how the app could be notified when a certain event (namely the geofence occurred) and the second only works every 15 minutes and focuses a different scenario as well.
Does somebody knows a simple way how I can ensure that a request was processed even when there is a bad internet connection (or even none at the moment) while allowing the users to minimize or even close the app?
Future _processUploadQueue() async {
int retryCounter = 0;
Future.doWhile(() {
if(retryCounter == 10){
print('Abborted after 10 tries');
return false;
}
if (_request.uploaded) {
print('Upload ready');
return false;
}
if(! _request.uploaded) {
_networkService.sendRequest(request: _request.entry)
.then((id){
print(id);
setState(() {
_request.uploaded = true;
});
}).catchError((e) {
retryCounter++;
print(e);
});
}
// e ^ retryCounter, min 0 Sec, max 10 minutes
int waitTime = min(max(0, exp(retryCounter)).round(), 600);
print('Waiting $waitTime seconds till next try');
return new Future.delayed(new Duration(seconds: waitTime), () {
print('waited $waitTime seconds');
return true;
});
})
.then(print)
.catchError(print);
}
You can use the plugin shared_preferences to save each HTTP response to the device until the upload completes successfully. Like this:
requests: [
{
id: 8eh1gc,
request: "..."
},
...
],
Then whenever the app is launched, check if any requests are in the list, retry them, and delete them if they complete. You could also use the background_fetch to do this every 15 minutes.

Testing iOS: Calling init() the first time an app is opened triggers 2 PURCHASE_CANCELLED events

The ios user account password pops up also. This doesn't happen if called again while app is still opened, but repeats if app is closed and reopened.
Thanks
Sounds like you have unfinished purchases that you need to finish.
Purchases will remain in a pending state until your application calls finish on them. This is to ensure that your application processes and verifies a purchase.
You should call InAppBilling.service.finishPurchase when you have process a purchase and either delivered the product or handled the cancellation/failure:
https://gist.github.com/marchbold/851359b9e456e1a85d65#file-distriqt-extension-inappbilling-makepurchase-as
private function purchase_cancelledHandler( event:PurchaseEvent ):void
{
// This transaction was cancelled so you should notify your user and finish the purchase
trace( "purchase cancelled" + event.errorCode );
if (event.data && event.data.length > 0)
InAppBilling.service.finishPurchase( event.data[0] );
}
At start up you can retrieve the pending purchases after the SETUP_SUCCESS event:
private function setupSuccessHandler( event:InAppBillingEvent ):void
{
var pending:Array = InAppBilling.service.getPendingPurchases();
// Iterate over and handle as required
}
http://docs.airnativeextensions.com/inappbilling/docs/com/distriqt/extension/inappbilling/InAppBilling.html#getPendingPurchases()