Forum Xamarin.iOS

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

Unable to create view controller in Async Task

Kev84Kev84 FRMember
edited June 2016 in Xamarin.iOS

Hi Experts,

I'm going to do my best in english to explain the problem.
I'm currently trying Xamarin to check if we will migrate our IOS app and Android App on this, because it seems to be really nice.

The small test I'm doing is just test my simple login page.

I'm really new to CSharp, so sorry in advance if I ask stupid and simple questions...

[IMPLEMENTATION]
So this is what I've done using RestSharp (Rest library client) library from nuggets. I have a shared project which will be used in Android App and IOs app (in order to make the job only once). I'm working with Xamarin Studio on Mac.

In my LoginApiManager.cs I have a "basic" implementation like this:

public interface UserConnectionStates
    {
        //Regroup all state created above
        void userIsConnected(User user);
        void userConnectionFailed(IRestResponse<User> response);
        void userDisconnected();
}

public class LoginApiManager : RestManager
{
    //Used for instance and shared element
    public static LoginApiManager instance;

    //Permits to store the information about the current loged in user
    private User loggedInUser;

    //All listeners for that class
    private List<UserConnectionStates> userConnectionStatesListeners = new List<UserConnectionStates>();

    public LoginApiManager()
    {
        //Create the the Rest Client to make Api Calls
        base.client = new RestClient();
        client.BaseUrl = new Uri(apiPath);
        client.Authenticator = new HttpBasicAuthenticator(null, null);
    }

    public static LoginApiManager getInstance()
    {
        if (instance != null)
        {
            return instance;
        }
        else
        {
            return new LoginApiManager();
        }
    }

    //------- UserConnectionStates Listeners accessors --------
    public void addUserConnectionStatesListener(UserConnectionStates listener)
    {

        if (!userConnectionStatesListeners.Contains(listener))
        {
            userConnectionStatesListeners.Add(listener);
        }

    }

    public void removeUserConnectionStatesListener(UserConnectionStates listener)
    {
        if (userConnectionStatesListeners.Contains(listener))
        {
            userConnectionStatesListeners.Remove(listener);
        }
    }

    /*
     * Permits to connect a user with a given username and password. It will
     * call the API and return a User Object containing all information needed
     * for connection, including token.
     */
    public void connectUserWithUserNameAndPassword(string username, string password)
    {

        var request = new RestRequest(Method.POST);
        request.Resource = "/myApiEndpoints/";

        //Add it to the request
        request.AddParameter("username", username);
        request.AddParameter("password", password);

        //Execute the query
        client.ExecuteAsync<User>(request, (response) =>
        {
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                //We serialize the user from response
                loggedInUser = response.Data;
                foreach (var l in this.userConnectionStatesListeners)
                {
                    l.userIsConnected(response.Data);
                }

            }
            else
            {
                //We send error to the code
                foreach(var l in this.userConnectionStatesListeners)
                {
                    l.userConnectionFailed(response);
                }
            }

        });
    }
}

...Ok, let's continue, my LoginViewController implement the interface UserConnectionStates and reference himself to the listeners list in order to be informed about the different connection states modifications. Just like this:

in my LoginController.cs

public partial class LoginController : UIViewController, UserConnectionStates
{
    //Some code here ...

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        loginManager = LoginApiManager.getInstance();
        loginManager.addUserConnectionStatesListener(this); //we create a listener for information about user connection

    }

    //Here comme the interface methods...

    //Interface for listening user connection state
    public void userIsConnected(User user)
    {
        var alertController = UIAlertController.Create("Connexion", "Connexion Success!",UIAlertControllerStyle.Alert);
        alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, null));
        this.PresentViewController(alertController, true, null);

    }

    public void userConnectionFailed(IRestResponse<User> response)
    {
        var alert= UIAlertController.Create("Connexion",  "Unable to connect", UIAlertControllerStyle.Alert);
        alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, null));
        PresentViewController(alert, animated: true, completionHandler: null);
    }

    public void userDisconnected()
    {

    }

}

[PROBLEM]
... Here is my problem, when the client.ExecuteAsync(request, (response) is called, as an example for a success, it will call the interface method implemented by the listener object public void userIsConnected(User user) of my LoginController, but because it's called in an other thread it stops at var alertController = UIAlertController.Create("Connexion", "Connexion Success!",UIAlertControllerStyle.Alert);: nos crashs just stop and do nothing.

I made a test by using a simple IRestResponse response = client.Execute(request); and it works because the method is synchronous and called in the same Thread, but it locks the UI until task is done...

What is the best way in CSharp or Xamarin to implement this kind of patterns? If there is another way I'll learn it, but because I'm new to this I don't know what are the best practice for this kind of things and I didn't find this in documentation (it's for sure in it, but don't find it sorry).

Many thanks in advance for your help ,

Regards,
Kevin

Answers

  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭
    edited June 2016

    Try wrapping you code in an InvokeOnMainThread.

    public void UserIsConnected(User user)
    {
        InvokeOnMainThread(()=>{
           var alertController = UIAlertController.Create("Connexion", "Connexion Success!", UIAlertControllerStyle.Alert);
           alertController.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Cancel, null));
    
           this.PresentViewController(alertController, true, null);
        });
    }
    

    I would also like to mention that in C# public method starts with upper case.

  • JordanRestifoJordanRestifo USMember
    edited June 2016

    If you don't know C# I would not recommend you port your apps to Xamarin. While it has helped me tremendously in some ways, there are way too many negatives in my opinion. For my next project I do not intend on using it. Regressions are quite common (for instance Xamarin iOS 9.8 broke the tint color on my TabbedPages, and suddenly I am having issues with pop-in on my alerts which forced me to switch to CRSAlertView). Support is nearly non existent. Do not expect to have your questions solved here, most questions go ignored unless the answer is easy or obvious. The garbage collector is really bad compared to the GC in actual .NET platforms or even other Mono implementations, and god forbid you start integrating unmanaged or even IDisposable objects (hint - using statements rarely are collected as expected). Some classes do not work as expected or as documented (recently I found calling Close on TcpClients does not work as it does in other platforms). You WILL have problems with provisioning profiles. Examples show the use of lambdas everywhere but apparently the implementation of them is quite spotty as they are very likely to lead to unexplained NullReferenceExceptions (particularly on Android in my experience) or objects that never get freed, either due to a problem with creating the closures themselves or the aforementioned garbage collector issues. These are not even close to all the issues I have encountered in as little as two months.

    I love C#. I used to love Xamarin. But there are too many bugs in both the tooling and the actual implementation for me to recommend it. If you already have native apps, keep them that way.

Sign In or Register to comment.