So I have this method this uses a library to generate an external webpage to allow the user to enter their Facebook credentials for verification. Upon entering it we are creating a profile based on the that user's account and saving it to a database. Once we save it to the database we are expecting a boolean to represent whether or not the account was unique. We need some way to wait for the database to complete all of its work before continuing execution.
I've tried several different methods including doing all the work on a new Thread (which won't work since all the UI needs to be on the main thread) as well as using AutoResetEvent
to wait for the auth.Completed
event to fire. But when using this it will usually block the thread and not even present our external webpage for the user.
Whats making this more complicated is trying to deal with all the different events without getting ahead of ourselves. So first we need to wait for auth.Completed
to fire, then from inside that event we need to wait for asynchronous request.GetResponseAsync().ContinueWith<Task<bool>>(async t => ...
event to finish, and finally wait for the database to complete its task and somehow push the return value all the way back to the calling procedure.
As of right now, this code will perform all the desired functions, but will return false since we aren't waiting for any of the events to fire.
public bool LoginFacebook(bool isLinkedAccount)
{
#if __IOS__
UIKit.UIWindow window = UIKit.UIApplication.SharedApplication.KeyWindow;
UIKit.UIViewController viewController = window.RootViewController;
#endif
var auth = new OAuth2Authenticator(
clientId: "***********",
scope: "email",
authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"),
redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html")
);
#if __ANDROID__
Forms.Context.StartActivity(auth.GetUI(Android.App.Application.Context));
#elif __IOS__
if (viewController != null)
{
while (viewController.PresentedViewController != null)
viewController = viewController.PresentedViewController;
viewController.PresentViewController(auth.GetUI(), true, null);
}
#endif
// If authorization succeeds or is canceled, .Completed will be fired.
auth.AllowCancel = true;
auth.Completed += (sender, eventArgs) =>
{
#if __IOS__
viewController.DismissViewController(true, null);
#endif
if (eventArgs.IsAuthenticated)
{
var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me"), null, eventArgs.Account);
request.GetResponseAsync().ContinueWith<Task<bool>>(async t =>
{
if (t.IsFaulted)
Console.WriteLine("Error: " + t.Exception.InnerException.Message);
else
{
try
{
string content = t.Result.GetResponseText();
Console.WriteLine(content);
JObject user = JsonConvert.DeserializeObject<JObject>(content);
Facebook profile = new Facebook();
profile.uuid = user["id"].ToString();
profile.firstname = user["first_name"].ToString();
profile.lastname = user["last_name"].ToString();
profile.email = user["email"].ToString();
profile.url = user["link"].ToString();
//Get picture
profile.photo = "https://graph.facebook.com/" + profile.uuid + "/picture?type=large";
if (!isLinkedAccount)
{
//App.AnimateLoading(LoadingImage, OverlayImage, LoadingMessage);
return await DatabaseInstance.InsertSocialProfile(SocialNetworks.Facebook, profile, true);
}
else
{
await DatabaseInstance.LinkSocialAccountToProfile(App.UserProfile, profile, SocialNetworks.Facebook);
return true;
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
return false;
});
}
};
return false;
}
You can use
TaskCompletionSource
to represent an awaitable task, to negotiate between the event handler and the rest of anasync
method.For brevity and clarity, I've trimmed all the
#ifdef
code from your example, focusing just on the core. With that in mind, here's what that would look like:Naturally, with the change of this method to be an
async
method, the caller now can wait asynchronously on the result, avoiding the blocking of the main thread.Of course, lacking a good, minimal, complete code example to start with, the above is just browser-edited code. I did not compile it, never mind test it. But the above is the general idea. I assume you can incorporate the concept in your actual code.