.NET MAUI, how to get data from SecureStorage in constructor - c#

I am struggling to get JWT from SecureStorage, as it's Get method is async, and we all know that constructor doesn't support async calls.
UPDATE:
What I want to do is check if I have a token and at the app start show the LoginPage or the MainPage.
I tried something like this:
public AppShell()
{
JWTokenModel jwt = null;
Task.Run(async () =>
{
jwt = await StorageService.Secure.GetAsync<JWTokenModel>(StorageKeys.Secure.JWT);
});
InitializeComponent();
RegisterRoutes();
shellContent.Title = "Amazons of Volleyball";
if (jwt is null || jwt?.Expiration < DateTime.Now)
{
shellContent.Route = PageRoutes.LoginPage;
shellContent.ContentTemplate = new DataTemplate(typeof(LoginPage));
}
else
{
shellContent.Route = PageRoutes.HomePage;
shellContent.ContentTemplate = new DataTemplate(typeof(MainPage));
}
}
private void RegisterRoutes()
{
Routing.RegisterRoute(PageRoutes.LoginPage, typeof(LoginPage));
Routing.RegisterRoute(PageRoutes.HomePage, typeof(MainPage));
Routing.RegisterRoute(PageRoutes.DetailsPage, typeof(PlayerDetailsPage));
Routing.RegisterRoute(PageRoutes.AddOrUpdatePage, typeof(AddOrUpdatePlayer));
}
When it hits the StorageService.Secure.GetAsync method's line, where I wan't to get the data like
public static async Task<T> GetAsync<T>(string key)
{
try
{
var value = await SecureStorage.Default.GetAsync(key);
if (string.IsNullOrWhiteSpace(value))
return (T)default;
var data = JsonSerializer.Deserialize<T>(value);
return data;
}
catch(Exception ex)
{
return (T)default;
}
}
it simple jumps out of the method.
UPDATE: I update the code suggested by ewerspej. The error still stands, when the SecureStore tries to get the value it jumps out from the method, no exception and I got the following error:
System.InvalidOperationException: 'No Content found for ShellContent, Title:, Route D_FAULT_ShellContent2'
The updated code:
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
RegisterRoutes();
SetShellContentTemplate();
}
private async void SetShellContentTemplate()
{
var hasValidJWT = await LoadTokenAsync();
if (hasValidJWT)
{
shellContent.ContentTemplate = new DataTemplate(typeof(MainPage));
shellContent.Route = PageRoutes.HomePage;
shellContent.Title = "Amazons of Volleyball";
}
else
{
shellContent.ContentTemplate = new DataTemplate(typeof(LoginPage));
shellContent.Route = PageRoutes.LoginPage;
shellContent.Title = "Amazons of Volleyball";
}
}
private async Task<bool> LoadTokenAsync()
{
var jwt = await StorageService.Secure.GetAsync<JWTokenModel>(StorageKeys.Secure.JWT);
return !(jwt is null || jwt?.Expiration < DateTime.Now);
}
private void RegisterRoutes()
{
Routing.RegisterRoute(PageRoutes.LoginPage, typeof(LoginPage));
Routing.RegisterRoute(PageRoutes.HomePage, typeof(MainPage));
Routing.RegisterRoute(PageRoutes.DetailsPage, typeof(PlayerDetailsPage));
Routing.RegisterRoute(PageRoutes.AddOrUpdatePage, typeof(AddOrUpdatePlayer));
}
}
UPDATE 2:
Moved the logic to App class:
public static class PageRoutes
{
public static string LoginPage = "login";
public static string HomePage = "home";
public static string AddOrUpdatePage = "add-or-update";
public static string DetailsPage = "/details";
}
public partial class App : Application
{
private readonly ISecurityClient securityClient;
public App(ISecurityClient securityClient)
{
this.securityClient = securityClient;
InitializeComponent();
SetStartPage();
}
private async void SetStartPage()
{
var hasValidJWT = await ReatJwtAsync();
MainPage = hasValidJWT ?
new AppShell() :
new LoginPage(securityClient);
}
private async Task<bool> ReatJwtAsync()
{
var jwt = await StorageService.Secure.GetAsync<JWTokenModel>(StorageKeys.Secure.JWT);
return !(jwt is null || jwt?.Expiration < DateTime.Now);
}
}
public partial class AppShell : Shell
{
public AppShell()
{
RegisterRoutes();
InitializeComponent();
}
private void RegisterRoutes()
{
Routing.RegisterRoute(PageRoutes.LoginPage, typeof(LoginPage));
Routing.RegisterRoute(PageRoutes.HomePage, typeof(MainPage));
Routing.RegisterRoute(PageRoutes.DetailsPage, typeof(PlayerDetailsPage));
Routing.RegisterRoute(PageRoutes.AddOrUpdatePage, typeof(AddOrUpdatePlayer));
}
}
AppShell.xaml
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiUI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiUI"
xmlns:pages="clr-namespace:MauiUI.Pages"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Amazons of Volleyball"
ContentTemplate="{DataTemplate local:MainPage}"
Route="HomePage" />
</Shell>
Still not reading a token successfully. But looking at the output I can see that Thread #8 started (might have some impact).
Now I am getting a new error:
System.NotImplementedException: 'Either set MainPage or override CreateWindow.'
any idea?
thnx

Instead of loading the token directly inside the constructor, you should defer it to an asynchronous method which you can still call from within the constructor, but you cannot await it and you shouldn't be using a blocking call, either.
You could do this in the App.xaml.cs, which is a common place for loading data when an app starts. While the data is loading, you can set the MainPage object to some type of loading page and once the token was loaded, you can set the MainPage to the AppShell or a LoginPage instance, e.g. like this:
public App()
{
InitializeComponent();
MainPage = new LoadingPage();
Load();
}
private async void Load()
{
if (await LoadTokenAsync())
{
MainPage = new AppShell();
}
else
{
MainPage = new LoginPage();
}
}
private async Task<bool> LoadTokenAsync()
{
var jwt = await StorageService.Secure.GetAsync<JWTokenModel>(StorageKeys.Secure.JWT);
return !(jwt is null || jwt?.Expiration < DateTime.Now);
}
This is just to demonstrate how you could solve the problem. You shouldn't do this in AppShell.xaml.cs but rather in the App.xaml.cs. Obviously, you'll need to store the token in memory somewhere.

Related

ChatMessages.Add(message) is not hit

I'm getting started with simple signalR application. When user puts his "name" and "room". It is sent to my hub
//This is Index page
public async Task<ActionResult> OnPost()
{
UserConnection userConnection = new()
{
Name = UserInput,
Room = JoinRoomInput
};
await _hubConnection.SendAsync("JoinRoom", userConnection);
return RedirectToPage("ChatGroup");
}
my chat looks like this
public class ChatHub : Hub<IChatClient>
{
private readonly string _botUser;
public ChatHub()
{
_botUser = "MyChat Bot";
}
public async Task JoinRoom(UserConnection userConnection)
{
await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.Room);
await Clients.Group(userConnection.Room).ReceiveMessage(_botUser, $"{userConnection.Name} has joined {userConnection.Room}");
}
public async Task SendMessage(UserConnection userConnection, string message)
{
await Clients.Group(userConnection.Room).ReceiveMessage($"{userConnection.Name} : ", message);
}
}
So then, the use posts his details and hit joinroom. He is redirected to another razor page called Chatgroup.In this page, He should get the message as defined in "JoinRoom" in ChatHub.
//This is Chatgroup page
private readonly HubConnection _hubConnection;
public List<string> ChatMessages { get; set; }
public ChatGroupModel(HubConnection hubConnection)
{
_hubConnection = hubConnection;
}
public void OnGet()
{
_hubConnection.On<string>("ReceiveMessage", (message) => //Breakpoint hits here
{
ChatMessages.Add(message); // When placed a breakpoint here, It is skipped.
});
}
I'm getting ChatMessages is Null error.
So my question is how will my client side code _hubConnection.On<> get the response from chathub?
I think this is the issue.
I've registered my signalr in startup class like this
services.AddTransient<HubConnection>((ChatClient) => {
var hubConnection = new HubConnectionBuilder().WithUrl("https://localhost:44389/chathub").Build();
hubConnection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await hubConnection.StartAsync();
};
hubConnection.StartAsync();
return hubConnection;
});
I get a null exception error in my razor page. I think it's because _hubConnection.On<>... skips chatmessage.add.
#foreach(var messages in Model.ChatMessages) //ChatMessages is Null
{
<div>
#messages
</div>
}

Trigger Async Function on Other Pages by Calling it in Non-Async Constructor in C#

In my App, there is an Async Function ProcessOffer(). When I called it in Constructor as ProcessOffer, it works but synchronously. I want to call this Function in constructor asynchronously.
The ProcessOffer() is a function implemented in CredentialViewModel, but I want that, It should be triggered asynchronously everywhere on the App (IndexViewModel e.t.c).
If I'm on the IndexPage, and Web Application sends a request to Mobile Application, ProcessOffer(), should be triggered.. actually what ProcessOffer does is, that it asks the user to enter a PIN, if it's correct, it sends back a response to the Web Application.
I've tried answers from other Posts, but they returned the Error Autofac.Core.DependencyResolutionException: 'An exception was thrown while activating App.Name, when I sends a request to Mobile App from Web Application.
The Solutions I tried.
1- https://stackoverflow.com/a/64012442/14139029
2- Task.Run(() => ProcessOffer()).Wait();
3- ProcessOffer().GetAwatier().GetResult();
CredentialViewModel.cs
namespace Osma.Mobile.App.ViewModels.Credentials
{
public class CredentialViewModel : ABaseViewModel
{
private readonly CredentialRecord _credential;
private readonly ICredentialService _credentialService;
private readonly IAgentProvider _agentContextProvider;
private readonly IConnectionService _connectionService;
private readonly IMessageService _messageService;
private readonly IPoolConfigurator _poolConfigurator;
[Obsolete]
public CredentialViewModel(
IUserDialogs userDialogs,
INavigationService navigationService,
ICredentialService credentialService,
IAgentProvider agentContextProvider,
IConnectionService connectionService,
IMessageService messageService,
IPoolConfigurator poolConfigurator,
CredentialRecord credential
) : base(
nameof(CredentialViewModel),
userDialogs,
navigationService
)
{
_credential = credential;
_credentialService = credentialService;
_agentContextProvider = agentContextProvider;
_connectionService = connectionService;
_messageService = messageService;
_poolConfigurator = poolConfigurator;
_credentialState = _credential.State.ToString();
if (_credentialState == "Offered")
{
ProcessOffer();
}
}
[Obsolete]
public async Task ProcessOffer()
{
foreach (var item in _credential.CredentialAttributesValues)
{
await SecureStorage.SetAsync(item.Name.ToString(), item.Value.ToString());
}
var RegisteredPIN = await SecureStorage.GetAsync("RegisteredPIN");
string PIN = await App.Current.MainPage.DisplayPromptAsync("Enter PIN", null, "Ok", "Cancel", null, 6, Keyboard.Numeric);
if (PIN == RegisteredPIN)
{
try
{
//await _poolConfigurator.ConfigurePoolsAsync();
var agentContext = await _agentContextProvider.GetContextAsync();
var credentialRecord = await _credentialService.GetAsync(agentContext, _credential.Id);
var connectionId = credentialRecord.ConnectionId;
var connectionRecord = await _connectionService.GetAsync(agentContext, connectionId);
(var request, _) = await _credentialService.CreateRequestAsync(agentContext, _credential.Id);
await _messageService.SendAsync(agentContext.Wallet, request, connectionRecord);
await DialogService.AlertAsync("Request has been sent to the issuer.", "Success", "Ok");
}
catch (Exception e)
{
await DialogService.AlertAsync(e.Message, "Error", "Ok");
}
}
else if (PIN != RegisteredPIN && PIN != null)
{
DialogService.Alert("Provided PIN is not correct");
}
}
#region Bindable Command
[Obsolete]
public ICommand ProcessOfferCommand => new Command(async () => await ProcessOffer());
public ICommand NavigateBackCommand => new Command(async () =>
{
await NavigationService.PopModalAsync();
});
#endregion
#region Bindable Properties
private string _credentialState;
public string CredentialState
{
get => _credentialState;
set => this.RaiseAndSetIfChanged(ref _credentialState, value);
}
#endregion
}
}
IndexViewModel.cs
namespace Osma.Mobile.App.ViewModels.Index
{
public class IndexViewModel : ABaseViewModel
{
private readonly IConnectionService _connectionService;
private readonly IMessageService _messageService;
private readonly IAgentProvider _agentContextProvider;
private readonly IEventAggregator _eventAggregator;
private readonly ILifetimeScope _scope;
public IndexViewModel(
IUserDialogs userDialogs,
INavigationService navigationService,
IConnectionService connectionService,
IMessageService messageService,
IAgentProvider agentContextProvider,
IEventAggregator eventAggregator,
ILifetimeScope scope
) : base(
"Index",
userDialogs,
navigationService
)
{
_connectionService = connectionService;
_messageService = messageService;
_agentContextProvider = agentContextProvider;
_eventAggregator = eventAggregator;
_scope = scope;
}
public override async Task InitializeAsync(object navigationData)
{
await base.InitializeAsync(navigationData);
}
public class Post
{
public string Success { get; set; }
public string firstname { get; set; }
}
[Obsolete]
public async Task ScanVerification(object sender, EventArgs e)
{
// Code
}
public async Task SettingsPage(SettingsViewModel settings) => await NavigationService.NavigateToAsync(settings, null, NavigationType.Modal);
#region Bindable Command
public ICommand SettingsPageCommand => new Command<SettingsViewModel>(async (settings) =>
{
await SettingsPage(settings);
});
[Obsolete]
public ICommand ScanVerificationCommand => new Command(async () => await ScanVerification(default, default));
#endregion
}
}
App.xml.cs
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Osma.Mobile.App
{
public partial class App : Application
{
public new static App Current => Application.Current as App;
public static IContainer Container { get; set; }
// Timer to check new messages in the configured mediator agent every 10sec
private readonly Timer timer;
private static IHost Host { get; set; }
public App()
{
InitializeComponent();
timer = new Timer
{
Enabled = false,
AutoReset = true,
Interval = TimeSpan.FromSeconds(10).TotalMilliseconds
};
timer.Elapsed += Timer_Elapsed;
}
public App(IHost host) : this() => Host = host;
public static IHostBuilder BuildHost(Assembly platformSpecific = null) =>
XamarinHost.CreateDefaultBuilder<App>()
.ConfigureServices((_, services) =>
{
services.AddAriesFramework(builder => builder.RegisterEdgeAgent(
options: options =>
{
options.AgentName = "Mobile Holder";
options.EndpointUri = "http://11.222.333.44:5000";
options.WalletConfiguration.StorageConfiguration =
new WalletConfiguration.WalletStorageConfiguration
{
Path = Path.Combine(
path1: FileSystem.AppDataDirectory,
path2: ".indy_client",
path3: "wallets")
};
options.WalletConfiguration.Id = "MobileWallet";
options.WalletCredentials.Key = "SecretWalletKey";
options.RevocationRegistryDirectory = Path.Combine(
path1: FileSystem.AppDataDirectory,
path2: ".indy_client",
path3: "tails");
// Available network configurations (see PoolConfigurator.cs):
options.PoolName = "sovrin-test";
},
delayProvisioning: true));
services.AddSingleton<IPoolConfigurator, PoolConfigurator>();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterAssemblyModules(typeof(CoreModule).Assembly);
if (platformSpecific != null)
{
containerBuilder.RegisterAssemblyModules(platformSpecific);
}
containerBuilder.Populate(services);
Container = containerBuilder.Build();
});
protected override async void OnStart()
{
await Host.StartAsync();
// View models and pages mappings
var _navigationService = Container.Resolve<INavigationService>();
_navigationService.AddPageViewModelBinding<MainViewModel, MainPage>();
_navigationService.AddPageViewModelBinding<RegisterViewModel, RegisterPage>();
_navigationService.AddPageViewModelBinding<IndexViewModel, IndexPage>();
_navigationService.AddPageViewModelBinding<SettingsViewModel, SettingsPage>();
_navigationService.AddPageViewModelBinding<CredentialsViewModel, CredentialsPage>();
_navigationService.AddPageViewModelBinding<CredentialViewModel, CredentialPage>();
if (Preferences.Get(AppConstant.LocalWalletProvisioned, false))
{
await _navigationService.NavigateToAsync<MainViewModel>();
}
else
{
await _navigationService.NavigateToAsync<ProviderViewModel>();
}
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
// Check for new messages with the mediator agent if successfully provisioned
if (Preferences.Get(AppConstant.LocalWalletProvisioned, false))
{
Device.BeginInvokeOnMainThread(async () =>
{
try
{
var context = await Container.Resolve<IAgentProvider>().GetContextAsync();
await Container.Resolve<IEdgeClientService>().FetchInboxAsync(context);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
});
}
}
protected override void OnSleep() =>
// Stop timer when application goes to background
timer.Enabled = false;
protected override void OnResume() =>
// Resume timer when application comes in foreground
timer.Enabled = true;
}
}
Look, you have public override async Task InitializeAsync(object navigationData) method in the IndexViewModel class. I suppose the framework invoke it to initialize the view model. So, thus CredentialViewModel inherits same ABaseViewModel anyway, why wouldn't you just override InitializeAsync in your CredentialViewModel and call ProcessOffer from it?
public CredentialViewModel(
IUserDialogs userDialogs,
INavigationService navigationService,
ICredentialService credentialService,
IAgentProvider agentContextProvider,
IConnectionService connectionService,
IMessageService messageService,
IPoolConfigurator poolConfigurator,
CredentialRecord credential
) : base(
nameof(CredentialViewModel),
userDialogs,
navigationService
)
{
_credential = credential;
_credentialService = credentialService;
_agentContextProvider = agentContextProvider;
_connectionService = connectionService;
_messageService = messageService;
_poolConfigurator = poolConfigurator;
_credentialState = _credential.State.ToString();
}
public override async Task InitializeAsync(object navigationData)
{
if (_credentialState != "Offered") return;
await ProcessOffer();
}
Anyway, you have to avoid calling asynchronous operations in constructors.
So in my View Models, if i need to initialize asynchronously I usually pass OnAppearing() to the view model instead of the constructor. Someone can tell me if this is unwise but it solved my needs.
Code Behind View:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CredentialView : ContentPage
{
public CredentialView()
{
InitializeComponent();
BindingContext = new CredentialViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
((CredentialViewModel)BindingContext).OnAppearing();
}
}
View Model:
public class CredentialViewModel : INotifyPropertyChanged
{
public CredentialViewModel ()
{
}
public async void OnAppearing()
{
await ProcessOffer();
}
}
Also just FYI, the biggest gotcha i've experienced with asynchronous code on xamarin is to use BeginInvokeOnMainThread. It's my understanding that touched UI should be executed on main thread! (probably why they call it the UI thread. haha)
Example:
Device.BeginInvokeOnMainThread(() =>
{
observableCollection.Clear();
});
As you've already asked before, kicking off async code in the constructor of a class can be a can of worms. You should instead consider doing either:
Starting ProcessOffer in a lifecycle method instead. For instance when the View is showing call ProcessOfferCommand.
Using fire and forget to not block the constructor: Task.Run(ProcessOffer) you should probably avoid this though.
Use something like NotifyTask Stephen Cleary describes here: https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/march/async-programming-patterns-for-asynchronous-mvvm-applications-data-binding you can find the complete code for it here: https://github.com/StephenCleary/Mvvm.Async/blob/master/src/Nito.Mvvm.Async/NotifyTask.cs
Use a CreateAsync pattern with a private constructor. However, this doesn't work well with Dependency Injection usually. Doing IO intensive work during resolution isn't really the correct place to do it.
In my opinion using either 1. or 3. would be the best solutions, perhaps leaning towards a combination of 1. using NotifyTask that can notify you when it is done loading.

UserDialogs Loading does not show up

I am trying to see Loading progress as follows, but it does not show up.
View.cs
ViewModel.SelectedCommand.Execute(null);
ViewModel.cs
public ICommand SelectedCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following does not show loading
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classroomService.GetClassRoomAsync(SelectedClassroom.Id);
ObservableCollection<ClassroomViewModel> class = new ObservableCollection<ClassroomViewModel>(task.ConvertAll(x => new ClassViewModel(x)));
}
});
}
}
Another example
public ICommand ReloadCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
await RefreshList();
});
}
}
// the following also does not show loading
private async Task RefreshList()
{
using (UserDialogs.Instance.Loading("Loading..."))
{
var task = await _classService.GetClasses();
}
}
If you are using Acr.MvvmCross.Plugins.UserDialogs see that it's depreated and you should use directly Acr.UserDialogs.
Check if you have correctly initialized it as follows:
You have to register it in App.cs of your PCL project:
Mvx.RegisterSingleton<IUserDialogs>(() => UserDialogs.Instance);
And init from the android platform project in your main activity:
UserDialogs.Init(() => Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity)
Another thing to take into account is that you should inject it in your constructor as an IUserDialogs (you can use the static Instance way but it adds more flexibility and it is more testable by injecting it):
private readonly IUserDialogs _dialogs;
public ProgressViewModel(IUserDialogs dialogs)
{
this._dialogs = dialogs;
}
and use it like
private async Task RefreshList()
{
using (this._dialogs.Loading("Loading..."))
{
try
{
var task = await this._classService.GetClasses();
}
catch(Exception exc)
{
// This is done only for debugging to check if here lies the problem
throw exc;
}
}
}
You can check if it is properly working by calling something like
public ICommand MyTestCommand
{
get
{
return new MvxAsyncCommand(async () =>
{
// the following should should Loading for 3 seconds
using (this._dialogs.Loading("Loading..."))
{
await Task.Delay(TimeSpan.FromSeconds(3));
}
});
}
}
HIH
I dont like this approuch but it works
Device.BeginInvokeOnMainThread(async () =>
{
try
{
using (UserDialogs.Instance.Loading(("Loading...")))
{
await Task.Delay(300);
await _syncController.SyncData();
//Your Service code
}
}
catch (Exception ex)
{
var val = ex.Message;
UserDialogs.Instance.Alert("Test", val.ToString(), "Ok");
}
});

How to consume ASP Rest web Api in Xamarin forms?

I have created a REST Api in ASP.Net web form. I can read and write data from MySql database using the API (I tested the Api using chrome's REST Client extension). Now I am trying to consume the Rest Api in my cross platform xamarin forms. I just added a button in my app and see whether it can retrieve data if I click the button. I inserted a breakpoint at the var Json to see whether I'm getting any data. But I am not able to retrieve it. I am running the Web Api in localhost. I run the app in VS Android emulator. Please guide me on how to properly consume the REST web service. Thank you.
Web Api
namespace WorkAppApi.Controllers
{
public class MachinesController : ApiController
{
// GET: api/Machines
public List<machines> Get()
{
DBConn db = new DBConn();
return db.getMachineList();
}
// GET: api/Machines/5
public machines Get(long id)
{
DBConn db = new DBConn();
machines m = db.getMachine(id);
return m;
}
// POST: api/Machines
public HttpResponseMessage Post([FromBody]machines value)
{
DBConn db = new DBConn();
long id;
id = db.addMachine(value);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri, String.Format("machines/{0}", id));
return response;
}
// PUT: api/Machines/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE: api/Machines/5
public void Delete(int id)
{
}
}
}
Function submit button.
namespace WorkApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
private string Uri = "http://192.168.0.124:59547/api/Machines/";
public TestPage ()
{
InitializeComponent ();
check();
}
private async Task submit(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var json = await httpClient.GetAsync(Uri);
}
}
}
this would be my answer:
namespace WorkApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestPage : ContentPage
{
private string Uri = "http://192.168.0.124:59547/api/";
List<Machine> machines;
public TestPage ()
{
InitializeComponent ();
check();
}
private async Task submit(object sender, EventArgs e)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = Uri // I have changed the Uri variabele, you should extend this class and give it the same base address in the constructor.
var resp= await httpClient.GetAsync("Machines");
if (resp.Result.IsSuccessStatusCode)
{
var repStr = resp.Result.Content.ReadAsStringAsync();
machines= JsonConvert.DeserializeObject<List<Machine>>(repStr.Result.ToString());
}
}
}
}

Xamarin Forms http async request

i am trying to make an asynchronous call to a webservice.
I would like to make this call when opening the app (App.xaml.cs).
According to the answer that comes back to me, it has to navigate to a particular page
But I do not work.
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
try
{
CheckLogin().Wait();
}
catch (Exception e)
{
var t = e;
}
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content); //crash without error, freeze
if (response.IsSuccessStatusCode)
{
var successResult = JsonConvert.DeserializeObject<HttpResponseMessage>(response.Content.ReadAsStringAsync().Result);
if (successResult != null)
{
//return true;
}
else
{
//return false;
}
}
}
catch (Exception e)
{
var t = e;
}
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<MainPage2>();
Container.RegisterTypeForNavigation<MainPage3>();
}
}
When does the postasync call does not go more forward, not I get no errors, but does not proceed.
But if I try the same code in an application console, everything works fine, why?
class Program
{
static void Main(string[] args)
{
Console.WriteLine("A");
CheckLogin().Wait();
Console.WriteLine("K");
Console.ReadKey();
}
private static async Task CheckLogin()
{
try
{
var login = new Login
{
Email = "test#test.com",
Password = "#test",
};
var client = new HttpClient { BaseAddress = new Uri("http://www.api.com/test/") };
var data = JsonConvert.SerializeObject(login);
var content = new StringContent(data, Encoding.UTF8, "application/json");
var response = await client.PostAsync(#"api/it-IT/auth/token", content);
if (response.IsSuccessStatusCode)
{
}
}
catch (Exception e)
{
var t = e;
}
}
}
If I try to do the same operation within a command with wait I do not work the same error, but if I do await, it will work fine, but in App.xaml.cs in OnInitialized() I can not put await
public DelegateCommand callCommand { get; set; }
public MainPage2ViewModel()
{
callCommand = new DelegateCommand(Call);
}
private void Call()
{
//await CheckLogin(); // work
CheckLogin().Wait(); // not work the same problem
var i = "pippo";
}
private async Task CheckLogin()
{
....
}
Is there anything to set with xamarin or with prism?
I've also the same strange error...
i fix with this workaround (use an async void that wrap async task)...
public App()
{
InitializeComponent();
Current.MainPage = new LoadingPage();
}
protected override void OnStart()
{
MagicInit();
base.OnStart();
}
public static async void MagicInit()
{
var f = await FileSystem.Current.LocalStorage.CreateFileAsync("db.sqlite", CreationCollisionOption.OpenIfExists);
DbConnection = f.Path;
await DataService.DbFill();
User = await DataService.Instance.Table<SpUser>().FirstOrDefaultAsync();
Current.MainPage = User != null ? (Page)new MainPage() : new LoginPage();
}

Categories

Resources