await Task.Delay() doesn't work on Xamarin.iOS using MVVMCross - c#

I am working on a Xamarin.iOS app using mvvm architecture. The problem is, that if I try to run await Task.Delay(2000) in the Initialize method of the main ViewModel class, the app stops on the LaunchScreen without any error messages.
Without that line the app works perfectly.
MainViewModel.cs
public override async Task Initialize()
{
await Task.Delay(2000);
}
Any Ideas what I'm doing wrong?
Edit:
Initialize is beeing called by MVVMCross after I call awaitNavigationService.Navigate()
public class CustomAppStart : MvxAppStart
{
public CustomAppStart(IMvxApplication application, IMvxNavigationService navigationService) : base(application, navigationService)
{
}
protected override async Task NavigateToFirstViewModel(object hint = null)
{
await NavigationService.Navigate<MainViewModel>();
}
}
And by the documentation I should be able to use await safely: MvvmCross ViewModel Lifecycle

As #kistelekig pointed out above, he was able to resolve it by placing the task delay in the ViewAppearing lifecycle function of the view model instead. So it would look like this
public override async Task ViewAppearing()
{
base.ViewAppearing()
await Task.Delay(2000);
...
}

Related

Background tasks are being queued and not executed

I've implemented the BackgroundQueue as explained here, and as shown:
public ActionResult SomeAction()
{
backgroundQueue.QueueBackgroundWorkItem(async ct =>
{
//Do some work...
});
return Ok();
}
I registered the BackgroundQueue with Autofac as:
builder.RegisterType<BackgroundQueue>()
.As<IBackgroundQueue>()
.SingleInstance();
So far so good. I call my controller action and the task is added to the queue. And there it stays without being executed.
So how do I get the task to execute?
The BackgroundQueue implementation that you took from the documentation is only one part to the solution: The background queue will just keep track of the jobs that you want to be executed.
What you will also need is right below that in the docs: The QueuedHostedService. This is a background service that gets registered with the DI container and is started when the application starts. From then on, it will monitor your BackgroundQueue and work off jobs as they get queued.
A simplified example implementation of this background service, without logging or error handling, could look like this:
public class QueuedHostedService : BackgroundService
{
private readonly IBackgroundQueue _backgroundQueue;
public QueuedHostedService(IBackgroundQueue backgroundQueue)
{
_backgroundQueue = backgroundQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _backgroundQueue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}

Cannot display an alert on app launch Xamarin Forms

I’m working on a Xamarin Forms app in which I want to perform some actions on launch. Part of the method that I want to call is responsible for checking whether some conditions are fulfilled, if not I want to display an alert (using DisplayAlert method) to inform user about the actions that he/she should take in order to allow for correct execution of apps functions. Unfortunately, on Android the alert is not being displayed, it doesn’t happen always, sometimes it gets displayed, but in most of the cases it doesn’t. After doing some research I think that the issue might be connected with threading. I added some breakpoints and I noticed that on Android the line of code responsible for displaying an alert is being executed before the page becomes visible. On iOS everything works fine (alert is being displayed when around half of the UI is shown). I have tried to execute everything from the main thread. On Android it helped partially, alert is being displayed correctly in more cases, although I still didn’t achieve 100% accuracy. Moreover, on iOS mentioned changes caused an app to get stuck on splash screen. Below there are two versions of the code that I’ve tried to use.
First the base code that I've started with:
public partial class App : Application
{
MainPage mainPage;
public static string DatabaseLocation = string.Empty;
public App(string databaseLocation)
{
//All of the UI code is written in C# in MainPage initializer
mainPage = new MainPage();
MainPage = new NavigationPage(mainPage);
DatabaseLocation = databaseLocation;
}
protected override async void OnStart()
{
base.OnStart();
//in DoThings there is a code containing DisplayAlert
await mainPage.DoThings();
}
}
Below is version of the code in which I've tried to put everything on main thread:
public partial class App : Application
{
MainPage mainPage;
public static string DatabaseLocation = string.Empty;
public App(string databaseLocation)
{
Device.BeginInvokeOnMainThread(() =>
{
mainPage = new MainPage();
MainPage = new NavigationPage(mainPage);
});
DatabaseLocation = databaseLocation;
}
protected override async void OnStart()
{
base.OnStart();
Device.BeginInvokeOnMainThread(async () =>
await mainPage.DoThings());
}
}
I have also tried to run from main thread the line of code specifically responsible for displaying an alert but it also didn't help. DoThings is a method inside MainPage.
public async Task DoThings() {
if (somethingMissing()){
Device.BeginInvokeOnMainThread(async () =>
await DisplayAlert("Error", "Fix errors", "Ok"));
}
else
{
DoStuff();
}
}
In MainPage you can try this code
public async Task DoThings() {
if (somethingMissing()){
Device.BeginInvokeOnMainThread(async () =>
await App.Current.MainPage.DisplayAlert("Error", "Fix errors", "Ok"));
}
else
{
DoStuff();
}
}

Execute same function on every page in Xamarin.Forms

I have a function that I need to perform to do some checks (e.g: CheckForUpdate, CheckNetworkConnection, CheckUserAuthorization, ...) on every page appearing or somehow before user request completed.
so I made a c# class and called it BasePage.cs:
public static class BasePage
{
public static async void CheckForUpdate()
{
// actual codes to check for updates are not included here
// just a sample alert
await App.Current.MainPage.DisplayAlert("Update", "There is a new version avaiable to update, would you like to download?", "Download", "Skip");
}
}
and used it in my pages like below:
LoginPage.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
await Task.Run(() => BasePage.CheckForUpdate());
}
}
I don't know if this is best practice or not (I guess not) but it's not displaying alert anyhow.
so my question is what is the best way to execute functions on every page and why the code above doesn't work.
Your code doesn't seem to run on UI thread. Just use Device.BeginInvokeOnMainThread, try like below
protected override void OnAppearing()
{
Device.BeginInvokeOnMainThread(() => {
BaseClass.CheckForUpdate();
});
}
Hope it help you.

Service call in a page constructor in Xamarin Forms

I want to make a service call only once the page is loaded , but the page constructor doesn't support the async await pattern , so I have to make the same in OnAppearing with some checks so the service call is made only once.
What is best practise for the same i.e. if I want to make a service call only once.
You can use a task to do the job.
See:
public MyClass()
{
... // Your stuff goes here
Initialize();
}
private async void Initialize()
{
Task.Run(async () =>
{
await ServerObjectReference.AsyncMethod();
});
}

C# The await operator can only be used within an async method but method is async

Error in line: await instance.CreateFile(); The await operator can only be used within an async method. Consider marking this method with the async modifier and changing its return type to Task
It's my understanding that the method is already asynchronous, I'm not sure what I am doing wrong. This is on a UWP app in VS 2015.
public sealed partial class MainPage : Page
{
private List<Customer> Customers;
public MainPage()
{
this.InitializeComponent();
CustomerDataAccessLayer instance = new CustomerDataAccessLayer();
await instance.CreateFile();
}
}
public class CustomerDataAccessLayer
{
public StorageFile mfile;
public async Task CreateFile()
{
mfile = await ApplicationData.Current.LocalFolder.GetFileAsync("Customers.xml");
if (mfile == null)
{
mfile = await ApplicationData.Current.LocalFolder.CreateFileAsync("Customers.xml", CreationCollisionOption.ReplaceExisting);
}
else
{
}
return;
}
}
As others have noted, the error message is complaining that the calling method is not async, but constructors cannot be marked async.
The core problem is that your UI cannot load asynchronously - it must load immediately. I recommend initially loading some kind of "Loading..." state and also starting an asynchronous operation. Then, when that operation completes, update the UI to display the data.
To start an asynchronous action from a synchronous function (like a constructor), you can use a Task.
Task.Run(async () =>
{
//put your async code here
});
To await this Task, you have to use some more code.
Task t = new Task(() => { /*Your code*/ });
t.Wait();

Categories