Open
Description
Describe the bug 🐞
ReactiveUI.Maui On IOS and Android we are not able receive the command or Binding getting executed.
Below is the code Sinpet when i press login Button the command is not getting trigged once we port from xamarin to MAUI.
public partial class LoginPage : ContentPage, IViewFor<LoginViewModel>
{
/// <inheritdoc/>
public LoginViewModel ViewModel { get; set; }
/// <inheritdoc/>
object IViewFor.ViewModel { get => this.ViewModel; set { this.ViewModel = (LoginViewModel)value; } }
List<string> backimage = new List<string>();
int pickedIndex = 0;
/// <summary>
/// Initializes a new instance of the <see cref="LoginPage"/> class.
/// </summary>
public LoginPage()
{
backimage.Add("portalui_1");
backimage.Add("portalui_2_v2");
backimage.Add("portalui_3_v2");
backimage.Add("portalui_4_v2");
backimage.Add("portalui_5_v2");
backimage.Add("portalui_6_v2");
backimage.Add("portalui_7");
backimage.Add("portalui_8");
Random random = new Random();
pickedIndex = random.Next(backimage.Count);
this.InitializeComponent();
this.BindingContext = ViewModel;
if (pickedIndex >= 0)
this.BackgroundImageSource = backimage[pickedIndex];
if (Device.RuntimePlatform == Device.Android)
FaceID.Source = "fingerprint.svg";
this.WhenActivated(disposables =>
{
this.BindCommand(
this.ViewModel,
vm => vm.LoginCommand,
v => v.LoginButton,
nameof(this.LoginButton.Clicked))
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.EmailAddress,
v => v.EmailAddress.Text)
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.Versionumber,
v => v.version.Text)
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.IsFaceEnabledChecked,
v => v.FaceEnableChecked.IsToggled)
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.IsFaceSaved,
v => v.FaceID.IsVisible)
.DisposeWith(disposables);
this.BindCommand(
this.ViewModel,
vm => vm.FaceClickCommand,
v => v.FaceGesture)
.DisposeWith(disposables);
this.BindCommand(
this.ViewModel,
vm => vm.RestPasswordClickCommand,
v => v.RestPasswordGesture)
.DisposeWith(disposables);
this.BindCommand(
this.ViewModel,
vm => vm.ConcatusClickCommand,
v => v.ContactUsGesture)
.DisposeWith(disposables);
this.BindCommand(
this.ViewModel,
vm => vm.ForgotUsernameClickCommand,
v => v.ForgotUsernameGesture)
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.Enablefacemess,
v => v.EnbaleFace.Text)
.DisposeWith(disposables);
this.Bind(
this.ViewModel,
vm => vm.Password,
v => v.Password.Text)
.DisposeWith(disposables);
});
}
/// <inheritdoc/>
protected override async void OnAppearing()
{
base.OnAppearing();
if (this.Logo.IsVisible == false)
{
this.Logo.Opacity = 0;
this.Logo.IsVisible = true;
this.FormContainer.Opacity = 0;
this.FormContainer.IsVisible = true;
this.Logo.TranslationY = this.Logo.TranslationY + 200;
await this.Logo.FadeTo(1, 700, Easing.CubicInOut);
await this.Logo.TranslateTo(this.Logo.TranslationX, this.Logo.TranslationY - 200, 500, Easing.CubicInOut);
await this.FormContainer.FadeTo(1, 700, Easing.CubicInOut);
}
try
{
if (this.ViewModel != null && Device.RuntimePlatform == Device.iOS)
{
await this.ViewModel.CheckForFingerprintAuthentication();
}
#if TODOMAUI
string latestVersionNumber = await CrossLatestVersion.Current.GetLatestVersionNumber();
string installedVersionNumber = CrossLatestVersion.Current.InstalledVersionNumber;
bool isupgradereq = false;
if (Device.RuntimePlatform == Device.iOS)
{
if (Convert.ToInt32(installedVersionNumber) < Convert.ToInt32(latestVersionNumber))
{
isupgradereq = true;
}
}
else
{
if (Convert.ToDecimal(installedVersionNumber) < Convert.ToDecimal(latestVersionNumber))
{
isupgradereq = true;
}
}
if (isupgradereq == true)
{
await DisplayAlert("New Version", "There is a new version of this app available. Would you like to update now?", "Update");
await CrossLatestVersion.Current.OpenAppInStore();
}
#endif
}
catch (Exception ex)
{
}
}
void FaceEnableChecked_Toggled(System.Object sender, ToggledEventArgs e)
{
try
{
if (e.Value == false)
{
Preferences.Set("IsFaceEnabledChecked", e.Value.ToString());
}
}
catch (Exception ex)
{
}
}
}
public class LoginViewModel : ReactiveObject, IEnableLogger, IRoutableViewModel
{
/// <inheritdoc/>
public string UrlPathSegment => "Login";
/// <inheritdoc/>
public IScreen HostScreen { get; }
private IAuthenticationService Service { get; }
private string emailAddress;
public string EmailAddress
{
get => emailAddress;
set => this.RaiseAndSetIfChanged(ref emailAddress, value);
}
private string password;
public string Password
{
get => password;
set => this.RaiseAndSetIfChanged(ref password, value);
}
private string enablefacemess;
public string Enablefacemess
{
get => enablefacemess;
set => this.RaiseAndSetIfChanged(ref enablefacemess, value);
}
private string versionumber;
public string Versionumber
{
get => versionumber;
set => this.RaiseAndSetIfChanged(ref versionumber, value);
}
private bool isFaceEnabledChecked = true;
public bool IsFaceEnabledChecked
{
get => isFaceEnabledChecked;
set => this.RaiseAndSetIfChanged(ref isFaceEnabledChecked, value);
}
private bool isFaceSaved;
public bool IsFaceSaved
{
get => isFaceSaved;
set => this.RaiseAndSetIfChanged(ref isFaceSaved, value);
}
public ReactiveCommand<Unit, Unit> LoginCommand { get; }
public ICommand RestPasswordClickCommand { get; set; }
public ICommand FaceClickCommand { get; set; }
public ICommand ConcatusClickCommand { get; set; }
public ICommand ForgotUsernameClickCommand { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="LoginViewModel"/> class.
/// </summary>
/// <param name="hostScreen"></param>
/// <param name="service"></param>
public LoginViewModel(IScreen hostScreen, IAuthenticationService service = null)
{
this.HostScreen = hostScreen;
this.Service = service ?? Locator.Current.GetService<IAuthenticationService>();
Versionumber = "Version " + VersionTracking.CurrentVersion;
if (Preferences.ContainsKey("IsFaceEnabledChecked"))
{
string IsFaceEnabled = Preferences.Get("IsFaceEnabledChecked", string.Empty);
this.IsFaceEnabledChecked = Convert.ToBoolean(Preferences.Get("IsFaceEnabledChecked", string.Empty));
}
string EmailAdd = Preferences.Get("EmailAddress", string.Empty);
string Passwd = Preferences.Get("Password", string.Empty);
bool IsFaceChecked = false;
if (Preferences.ContainsKey("IsFaceEnabledChecked"))
IsFaceChecked = Convert.ToBoolean(Preferences.Get("IsFaceEnabledChecked", string.Empty));
if (IsFaceChecked == true && !string.IsNullOrEmpty(EmailAdd) && !string.IsNullOrEmpty(Passwd))
IsFaceSaved = true;
this.RestPasswordClickCommand = ReactiveCommand.Create(() => this.RestPassword());
this.ConcatusClickCommand = ReactiveCommand.Create(() => this.ConcatUS());
this.ForgotUsernameClickCommand = ReactiveCommand.Create(() => this.ForgotUsername());
this.FaceClickCommand = ReactiveCommand.Create(() => this.CheckForFingerprintAuthentication());
var canLogin = this.WhenAnyValue(
vm => vm.EmailAddress,
vm => vm.Password,
(emailAddress, password) => !string.IsNullOrEmpty(emailAddress) && !string.IsNullOrEmpty(password));
this.LoginCommand = ReactiveCommand
.Create(
() =>
{
Loginasync(this.EmailAddress, this.Password, true);
}, canLogin);
this.LoginCommand
.ThrownExceptions
.Subscribe(ex =>
{
UserDialogs.Instance.Alert(ex.Message, "Error");
});
if (Device.RuntimePlatform == Device.Android)
{
CheckForFingerprintAuthentication();
}
}
private async Task Loginasync(string emailid, string password, bool isFirsttime = false)
{
try
{
var payload = new SignInPayload();
payload.RememberMe = true;
payload.UserName = emailid;
payload.Password = password;
UserDialogs.Instance.ShowLoading();
this.Service
.SignIn(payload)
.SubscribeOn(RxApp.TaskpoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Finally(() => UserDialogs.Instance.HideLoading())
.Subscribe(
content =>
{
Observable.FromAsync<string>(() => content.Content.ReadAsStringAsync())
.SubscribeOn(RxApp.TaskpoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(
async stringContent =>
{
if (content.IsSuccessStatusCode)
{
IEnumerable<string> values;
if (content.Headers.TryGetValues("Set-Cookie", out values))
{
App.cookiestring.Add(values);
}
Preferences.Set("EmailAddress", this.EmailAddress);
Preferences.Set("Password", password);
Preferences.Set("IsFaceEnabledChecked", this.IsFaceEnabledChecked.ToString());
if (isFirsttime == true)
await EnableFingerprintAuthentication();
#if TODOMAUI
this.HostScreen.Router.NavigateAndReset.Execute(new HomeViewModel(this.HostScreen)).Subscribe();
#endif
}
else
{
UserDialogs.Instance.Alert(stringContent, "Error");
}
},
e =>
{
var apiException = (ApiException)e;
UserDialogs.Instance.Alert(apiException.Content, "Error");
});
},
e =>
{
if (e is ApiException)
{
var apiException = (ApiException)e;
UserDialogs.Instance.Alert(apiException.Content, "Error");
}
else
{
UserDialogs.Instance.Alert(e.Message, "Error");
}
});
}
catch (Exception ex)
{
}
}
private async Task EnableFingerprintAuthentication()
{
if (this.IsFaceEnabledChecked == true)
{
var isFingerprintAvailable = await CrossFingerprint.Current.IsAvailableAsync(true);
var authenticationType = await CrossFingerprint.Current.GetAuthenticationTypeAsync();
string authenticationFailureMessage = authenticationType == Plugin.Fingerprint.Abstractions.AuthenticationType.Face ? "No Face ID Recognized." : "There are no fingerprints available.";
if (isFingerprintAvailable)
{
AuthenticationRequestConfiguration conf =
new AuthenticationRequestConfiguration("Authentication",
"Authenticate access to your personal data");
var result = await CrossFingerprint.Current.AuthenticateAsync(conf);
if (result.Authenticated == false)
{
}
}
}
}
/// <summary>
/// Checks for Finger Print Authentication
/// </summary>
/// <returns></returns>
public async Task CheckForFingerprintAuthentication()
{
this.EmailAddress = Preferences.Get("EmailAddress", string.Empty);
string Pwd = Preferences.Get("Password", string.Empty);
var authenticationType = await CrossFingerprint.Current.GetAuthenticationTypeAsync();
this.Enablefacemess = "Biometric ";
this.IsFaceEnabledChecked = Convert.ToBoolean(Preferences.Get("IsFaceEnabledChecked", string.Empty));
if (IsFaceEnabledChecked == true && !string.IsNullOrEmpty(this.EmailAddress) && !string.IsNullOrEmpty(Pwd))
{
var isFingerprintAvailable = await CrossFingerprint.Current.IsAvailableAsync(true);
string authenticationFailureMessage = authenticationType == Plugin.Fingerprint.Abstractions.AuthenticationType.Face ? "No Face ID Recognized." : "There are no fingerprints available.";
try
{
if (isFingerprintAvailable)
{
AuthenticationRequestConfiguration conf =
new AuthenticationRequestConfiguration("Authentication",
"Authenticate access to your personal data");
var result = await CrossFingerprint.Current.AuthenticateAsync(conf);
if (result.Authenticated)
{
this.EmailAddress = Preferences.Get("EmailAddress", string.Empty);
Pwd = Preferences.Get("Password", string.Empty);
if (!string.IsNullOrEmpty(this.EmailAddress) && !string.IsNullOrEmpty(Pwd))
{
await Loginasync(this.EmailAddress, Pwd, false); ;
}
}
else
{
UserDialogs.Instance.Alert("Unable to Access Biometric", "Error");
}
}
else
{
UserDialogs.Instance.Alert("Biometric is Not Supported", "Error");
}
}
catch (Exception exception)
{
Debug.WriteLine("FingerPrint Exception" + exception);
}
}
}
private void ForgotUsername()
{
#if TODOMAUI
this.HostScreen.Router.NavigateAndReset.Execute(new ForgotusernameViewModel(this.HostScreen)).Subscribe();
#endif
}
private void RestPassword()
{
#if TODOMAUI
this.HostScreen.Router.NavigateAndReset.Execute(new ResetPasswordViewModel(this.HostScreen)).Subscribe();
#endif
}
private void ConcatUS()
{
//Webview
//this.HostScreen.Router.NavigateAndReset.Execute(new ContactUSViewModel(this.HostScreen,false)).Subscribe();
PhoneDialer.Open("8665903550");
}
}
Step to reproduce
- Go to '...'
- Click on '....'
- Scroll down to '....'
- See error
Reproduction repository
https://github.com/reactiveui/ReactiveUI
Expected behavior
This should happen...
Screenshots 🖼️
No response
IDE
No response
Operating system
Apple
Version
No response
Device
IOS
ReactiveUI Version
18.4.26
Additional information ℹ️
No response