F# Event in class constructor - c#

I'm currently learning F# by re-doing a simple mobile application I did in C# and Xamarin.forms which has forgoal to connect a user with facebook and get his profile and posts.
I almost finish everything but I'm blocked. To do my connection to the facebook API in C#, I used the Xamarin.Auth library and I want to reuse this library in F#.
Here is my code for my LoginPage ViewModel in C#:
public class LoginPageViewModel : BaseViewModel
{
private readonly INavigationService _navigationService;
private readonly IConfiguration _config;
private LoginLogic _loginLogic;
public ICommand NavigateCommand { get; set; }
public OAuth2Authenticator MyAuthenticator;
public ICommand ConnectVerification { get; set; }
public bool CanSkipPage { get; set; }
public LoginPageViewModel(INavigationService navigationService, IConfiguration configuration)
{
if (navigationService == null) throw new ArgumentNullException("navigationService");
_navigationService = navigationService;
if (configuration == null) throw new ArgumentNullException("Configuration");
_config = configuration;
NavigateCommand = new RelayCommand(() => { _navigationService.NavigateTo(Locator.FacebookProfilePage); });
MyAuthenticator = new OAuth2Authenticator(
_config.facebookAppId,
_config.scope,
new Uri(_config.facebookAuthUrl),
new Uri(_config.facebookRedirectUrl),
null);
MyAuthenticator.Completed += OnAuthenticationCompleted;
MyAuthenticator.Error += OnAuthenticationFailed;
_loginLogic = SimpleIoc.Default.GetInstance<LoginLogic>();
this.ConnectVerification = new AsyncCommand(() => TokenVerification());
}
public async Task TokenVerification()
{
IsLoading = true;
if (await _loginLogic.CheckToken())
NavigateCommand.Execute(null);
IsLoading = false;
}
async void OnAuthenticationCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
IsLoading = true;
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthenticationCompleted;
authenticator.Error -= OnAuthenticationFailed;
}
await _loginLogic.SetTokenAsync(e.Account.Properties["access_token"]);
loginLogic.SetTokenAsync(e.Account.Properties["access_token"]);
NavigateCommand.Execute(null);
IsLoading = false;
}
void OnAuthenticationFailed(object sender, AuthenticatorErrorEventArgs e)
{
var authenticator = sender as OAuth2Authenticator;
if (authenticator != null)
{
authenticator.Completed -= OnAuthenticationCompleted;
authenticator.Error -= OnAuthenticationFailed;
}
}
}
My problem is to use Xamarin.Auth . I have to create a OAuth2Authenticator property that I initialize in the constructor of my class and then subscribe both EventHandler .Complete and .Error of this property to the two events OnAuthenticationCompleted and OnAuthenticationFailed in my class constructor and I've no idea how to do that in F#.
Right now, my F# class looks like this :
open Xamarin.Auth
open System
open GalaSoft.MvvmLight.Views
type LoginPageViewModel(navigationService: INavigationService) =
inherit ViewModelBase()
let mutable isLoading = false
let authenticator = new OAuth2Authenticator(config.facebookAppId,
config.scope,
new Uri(config.facebookAuthUrl),
new Uri(config.facebookRedirectUrl),
null)
member this.MyAuthenticator
with get() = authenticator
member this.IsLoading
with get() = isLoading
and set(value) =
isLoading <- value
base.NotifyPropertyChanged(<# this.IsLoading #>)
member this.TokenVerification() =
this.IsLoading <- true
if loginLogic.CheckToken()
then
navigationService.NavigateTo("FacebookProfilePage")
this.IsLoading <- false
But I don't know :
First, Where I should create my two methods OnAuthenticationCompleted and OnAuthenticationFailed, are they supposed to be methods of the class or not ?
Second, How To subscribe my OAuth2Authenticator.Complete to OnAuthenticationCompleted and OAuth2Authenticator.Error to OnAuthenticationFailed methods in my class constructor

You can add even handlers to your authenticator object using the following syntax:
let auth = OAuth2Authenticator("clientId", "scope", Uri("??"), Uri("??"))
auth.Error.Add(fun err ->
printfn "Error: %A" err)
auth.Completed.Add(fun res ->
let at = res.Account.Properties.["access_token"]
printfn "%A" at)
If you want to be able to add and remove the handler, then you need to create an explicit EventHandler value first:
let auth = OAuth2Authenticator("clientId", "scope", Uri("??"), Uri("??"))
let handler = EventHandler<AuthenticatorCompletedEventArgs>(fun _ res ->
let at = res.Account.Properties.["access_token"]
printfn "%A" at)
auth.Completed.AddHandler(handler)
auth.Completed.RemoveHandler(handler)
That said, if you simply translate the C# code to F#, then you probably won't gain much in this case. Your logic is quite imperative and things like the mutable isLoading field and adding/removing of event handlers is going to make your F# code quite ugly. If you want to develop mobile applications with F#, then I would instead recommend looking at Fabulous, which lets you write nice functional code.

Related

Mock interface implementation using Moq in C#

I am trying to write Xamarin.Forms UI tests using Moq to mock my authentication interface: [previous question][1]. I have refactored my application so that my SignIn(string username, string password) method is inside a class that implements the IAuthService. I am now having issues with mocking the IAuthService to essentially 'replace' the actual sign in verification that occurs when clicking the Sign In button. In my CloudAuthService class (which implements IAuthService), I am authenticating to Amazon Cognito, but I want to mock this result within the UI test so it is not calling the cloud service.
EDIT: after many suggestions, I have decided to include my current implementation below. This still doesn't appear to fully work despite the
output from Console.WriteLine(App.AuthApi.IsMockService()); within the BeforeEachTest() method results in true (as expected).
However, running the same thing within the App() constructor method results in false. So it doesn't appear to be running before the app actually starts, is there a way to have UITest code that runs before the app initializes?
LoginPage
[XamlCompilation(XamlCompilationOptions.Compile)]
public sealed partial class LoginPage
{
private readonly IBiometricAuthentication _bioInterface;
private static readonly Lazy<LoginPage>
Lazy =
new Lazy<LoginPage>
(() => new LoginPage(App.AuthApi));
public static LoginPage Instance => Lazy.Value;
private string _username;
private string _password;
private LoginPageViewModel _viewModel;
private IAuthService _authService;
public LoginPage(IAuthService authService)
{
InitializeComponent();
_authService = authService;
_viewModel = new LoginPageViewModel();
BindingContext = _viewModel;
}
private void LoginButtonClicked(object sender, EventArgs args)
{
_username = UsernameEntry.Text;
_password = PasswordEntry.Text;
LoginToApplication();
}
public async void LoginToApplication()
{
AuthenticationContext context = await _authService.SignIn(_username, _password);
}
}
App Class
public partial class App
{
public static IAuthService AuthApi { get; set; } = new AWSCognito()
public App()
{
Console.WriteLine(AuthApi.IsMockService())
// AuthApi = new AWSCognito(); // AWSCognito implements IAuthService
InitializeComponent();
MainPage = new NavigationPage(new LoginPage(AuthApi));
}
}
Test Class
class LoginPageTest
{
IApp app;
readonly Platform platform;
public LoginPageTest(Platform platform)
{
this.platform = platform;
}
[SetUp]
public void BeforeEachTest()
{
var mocker = new Mock<IAuthService>();
var response = new AuthenticationContext(CognitoResult.Ok)
{
IdToken = "SUCCESS_TOKEN"
};
mocker.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(() => new MockAuthService().SignIn("a", "a"));
mocker.Setup(x => x.IsMockService()).Returns(() => new MockAuthService().IsMockService());
App.AuthApi = mocker.Object;
Console.WriteLine(App.AuthApi.IsMockService());
app = AppInitializer.StartApp(platform);
}
[Test]
public void ClickingLoginWithUsernameAndPasswordStartsLoading()
{
app.WaitForElement(c => c.Marked("Welcome"));
app.EnterText(c => c.Marked("Username"), new string('a', 1));
app.EnterText(c => c.Marked("Password"), new string('a', 1));
app.Tap("Login");
bool state = app.Query(c => c.Class("ProgressBar")).FirstOrDefault().Enabled;
Assert.IsTrue(state);
}
}
Your problem seems to be that you've injected the mock after you run through the test. This means when it's executing it's using the original AuthService. If we rearrange the code to move the injection before anything gets executed we should see the result we expect:
// let's bring this mock injection up here
var mocker = new Mock<IAuthService>();
mocker.Setup(x => x.SignIn(It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(response)).Verifiable();
App.AuthApi = mocker.Object;
// now we try to login, which should call the mock methods of the auth service
app.WaitForElement(c => c.Marked("Welcome to Manuly!"));
app.EnterText(c => c.Marked("Username"), new string('a', 1));
app.EnterText(c => c.Marked("Password"), new string('a', 1));
app.Tap("Login");
var response = new AuthenticationContext(CognitoResult.Ok)
{
IdToken = "SUCCESS_TOKEN",
};
bool state = app.Query(c => c.Class("ProgressBar")).FirstOrDefault().Enabled;
Assert.IsTrue(state);
Now try executing it, and it should do as you desire.
EDIT:
As pointed out in the comments by Nkosi the static Auth service is set in the constructor preventing this.
SO this will need to be changed too:
public partial class App
{
public static IAuthService AuthApi { get; set; } =new AWSCognito(); // assign it here statically
public App()
{
// AuthApi = new AWSCognito(); <-- remove this
InitializeComponent();
MainPage = new NavigationPage(new LoginPage(AuthApi));
}
}

Adding Api interface in ViewModel constructor and navigation stops working Prism

I'm using VS 17 for Xamarin Forms. I've set up Prism in my Xamarin.Forms app and I just added a reference to my Api interface (in ViewModel Constructor) and it makes the app stop navigation to the second page. I need to do this in order to pass parameters etc. I followed this guide:
https://blog.qmatteoq.com/prism-for-xamarin-forms-basic-navigation-and-dependency-injection-part-2/
This is what I did to make the navigation stop working:
private readonly IService _Service;
private ObservableCollection<TodoItem> _topSeries;
public ObservableCollection<TodoItem> TopSeries
{
get { return _topSeries; }
set { SetProperty(ref _topSeries, value); }
}
This is the constructor:
public SecondPageViewModel(IService Service, INavigationService navigationService)
{
_Service = Service;
_navigationService = navigationService;
}
So I cant even reach the above viewmodel because of the above code that I added. I tried to put break points on the DelegateCommand (on first ViewModel) but it just stops after InitializeComponent(); and then nothing happens. No error messages! Thanks!
Update:
My Service class that fetches data:
public class Service : IService
{
public List<TodoItem> TodoList { get; private set; }
HttpClient client;
Service()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<TodoItem>> DataAsync()
{
TodoList = new List<TodoItem>();
var uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
TodoList = JsonConvert.DeserializeObject<List<TodoItem>>(content);
Debug.WriteLine(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return TodoList;
}
}
This is my App.Xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<View.MainPage, MainPageViewModel>();
containerRegistry.RegisterForNavigation<View.SecondPage, SecondPageViewModel>();
containerRegistry.Register<IService, Service>();
}
My Interface:
public interface IService
{
Task<List<TodoItem>> DataAsync();
}
This is how I navigate (click from listview):
private EventItem _selectedEvent { get; set; }
public EventItem SelectedEvent
{
get { return _selectedEvent; }
set
{
if (_selectedEvent != value)
{
if (Device.RuntimePlatform == Device.iOS)
{
_selectedEvent = null;
}
else
{
_selectedEvent = value;
}
NavigationParameters navParams = new NavigationParameters();
navParams.Add("PassedValue", _todoItem.name);
_navigationService.NavigateAsync("SecondPage", navParams);
}
}
}
Edit:
When I debug without the ApiService code the command is taking me to new new constructor in the new viewmodel. With the code it does not reach the contructor.
According to your code you have declared constructor like this:
Service()
{
// ...
}
You didn't set access modifier, therefore the default one is internal. Here is the definition:
Internal types or members are accessible only within files in the same
assembly.
Most likely you have your Service.cs declared in another Assembly and Prism can't access its constructor.
Your navigation doesn't work because dependency injection fails. To fix it, just change your access modifier to public:
public Service()
{
// ...
}

Auth0 LoginAsync error

I have to perform Auth0 authentication process and extract the token.
I have Authenticator class as below -
class Auth0Authenticator
{
public Auth0Authenticator() { performAuthentication(); }
public void performAuthentication()
{
Auth0Client auth0Client = new Auth0Client(new Auth0ClientOptions()
{
Domain = "mydomain",
ClientId = "clientid",
});
var extraParameters = new Dictionary<string, string>();
extraParameters.Add("connection", "parameter");
var result = auth0Client.LoginAsync(extraParameters: extraParameters);
}
}
while executing LoginAsync I am getting error - The calling thread must be STA, because many UI components require this.
even after creating STA thread or adding attribute [STAThread] not helping.
When i executed the same code in a simple dialog based application, code is successufully returning me the token. but putting the same code in my project(consists of MFC/C#/CLI) throwing error.
Can anyone help please?
This may be an XY problem. Auth0Client.LoginAsync is an async API and you are trying to invoke it in the constructor of your class. This can have negative consequences if there is code dependent on that function completing before being able to perform their functions.
Refactor the code to follow suggested syntax
public class Auth0Authenticator {
public Auth0Authenticator() {
//Subscribe to the event
autoAuthenticate += onAutoAuthenticating();
//raise event to allow async operation.
autoAuthenticate(this, EventArgs.Empty);
}
private event EventHandler autoAuthenticate = delegate { };
private async void onAutoAuthenticating(object sender, EventArgs args) {
await PerformAuthenticationAsync();
}
public async Task PerformAuthenticationAsync() {
Auth0Client auth0Client = new Auth0Client(new Auth0ClientOptions() {
Domain = "mydomain",
ClientId = "clientid",
});
var extraParameters = new Dictionary<string, string>();
extraParameters.Add("connection", "parameter");
var result = await auth0Client.LoginAsync(extraParameters: extraParameters);
//...do something with the result as needed
string access_token = result.AccessToken;
string refresh_token = result.RefreshToken;
//...
}
}

Async method that calls in ViewModel causes deadlock

I do requests to Github Api so I have async methods, these do this job. Before it, I always called they in method, that calls from command(actually DelegateCommand). But now I wanna do request in ViewModel because I need to display list on page. I am using Prism to wire view and viewmodel.
Because I can't make viewmodel async, I can't use await word, so I tried to do something like gets result from task, or task.wait. But with this I have the same result. My app stop works with white display when it did request. I read some info about that and I understood that call async method in not async method is bad, and it causes deadlock, but I don't know what to do with this. And I think deadlock causes that app stop works.
Here is method where app die:
public async Task<IEnumerable<RepositoryModel>> GetRepositoriesAsync()
{
try
{
var reposRequest = new RepositoryRequest { Sort = RepositorySort.FullName };
var gitHubRepos = await _gitHubClient.Repository.GetAllForCurrent(reposRequest); //async request, don't say about name convention, it is not my method.
var gitRemoteRepos = new List<RepositoryModel>();
foreach ( var repository in gitHubRepos )
{
var repos = new RepositoryModel();
repos.RepositoryTypeIcon = GetRepositoryTypeIcon(repository);
gitRemoteRepos.Add(repos);
}
return gitRemoteRepos;
}
catch ( WebException ex )
{
throw new Exception("Something wrong with internet connection, try to On Internet " + ex.Message);
}
catch ( Exception ex )
{
throw new Exception("Getting repos from github failed! " + ex.Message);
}
}
And here is viewmodel:
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public ObservableCollection<RepositoryModel> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
var task = _repositoriesManager.GetRepositoriesAsync();
//task.Wait();
Repositories = task.Result as ObservableCollection<RepositoryModel>;
}
}
I recommend using my NotifyTask<T> type, which provides a data-bindable wrapper around Task<T>. I explain this pattern more completely in my article on async MVVM data binding.
public class RepositoriesPageViewModel : BindableBase
{
private INavigationService _navigationService;
private readonly Session _session;
public NotifyTask<ObservableCollection<RepositoryModel>> Repositories { get; }
private readonly RepositoriesManager _repositoriesManager;
public RepositoriesPageViewModel(INavigationService navigationService, ISecuredDataProvider securedDataProvider)
{
_navigationService = navigationService;
var token = securedDataProvider.Retreive(ConstantsService.ProviderName, UserManager.GetLastUser());
_session = new Session(UserManager.GetLastUser(), token.Properties.First().Value);
var navigationParameters = new NavigationParameters { { "Session", _session } };
_repositoriesManager = new RepositoriesManager(_session);
Repositories = NotifyTask.Create(GetRepositoriesAsync());
}
}
private async Task<ObservableCollection<RepositoryModel>> GetRepositoriesAsync()
{
return new ObservableCollection<RepositoryModel>(await _repositoriesManager.GetRepositoriesAsync());
}
Note that with this approach, your data binding would use Repositories.Result to access the actual collection. Other properties are also available, most notably Repositories.IsCompleted and Respositories.IsNotCompleted for showing/hiding busy spinners.

MVVM light Messenger Action execute after first time Windows Uinversal 8.1

I have 2 classes and want to send object using Messenger while navigating from page to another and it works but only when navigate to the page and come back then try again not from first try.
ManivViewModel code:
public void GoToDetial(object parameter)
{
try
{
var arg = parameter as ItemClickEventArgs;
var item = arg.ClickedItem as Item;
Messenger.Default.Send<Item>(item, "Mess");
_navigationService.Navigate(typeof(DescriptionPage));
}
catch { }
}
DescriptionViewModel code:
public DescriptionViewModel(IRSSDataService rssService, INavigationService navigationService, IDialogService dialogService)
{
_dataService = rssService;
_navigationService = navigationService;
_dialogService = dialogService;
load();
LoadCommand = new RelayCommand(load);
GoToUrlCommand = new RelayCommand<object>(GoToUrl);
ShareSocialCommand = new RelayCommand(ShareSocial);
}
private void load()
{
Messenger.Default.Register<Item>(
this,
"Mess",
selectedItem =>
{
Item = selectedItem;
// Prepare content to share
RegisterForShare();
GetFromHTML(Item.Link);
});
}
I found it. I just need to pass in "true" to the Register call in the SimpleIoc to create the instance of the DescriptionViewModel immediately like this
SimpleIoc.Default.Register<DescriptionViewModel>(true);

Categories