Forum Xamarin.Forms

Check if device has connection during app run and how to handle if connection is lost

rozman50rozman50 Member ✭✭
edited December 2019 in Xamarin.Forms

What way would be the best way to check for internet connection while user is using app?

On startup I'm checking for internet connection with Xamarin.Essentials Connectivity and it works.

But if phone looses connection when app is fetching data from the internet System.Exception "unable to resolve host ..." is thrown and app crashes.
How to intercept this exception and how to handle it? Ideally I'd like to show NoConnectionPage (which I show if device doesn't have internet connection on app startup) or show an alert.

This is how I send request

            string token = await SecureStorage.GetAsync("auth_token").ConfigureAwait(false);
            var request = new HttpRequestMessage(HttpMethod.Get, BaseAddress);
            request.Headers.Accept.Clear();
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

            var response = await _client.SendAsync(request, CancellationToken.None).ConfigureAwait(false);
            if (response.IsSuccessStatusCode)
            {
            ...

HttpRequestException doesn't catch this exception!

Best Answers

  • rozman50rozman50 Member ✭✭
    Accepted Answer

    With help and suggestions of all you above, I created this. If there is anything that could be improved below, please share :)0

    I created custom HttpClient class, which check for internet connection before any http call is made:

    public class CustomHttpClient : System.Net.Http.HttpClient
    {
        private static HttpClient _client = new HttpClient();
        public CustomHttpClient()
        {
    
        }
    
        public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!CheckInternetConnection())
            {
                throw new TaskCanceledException("No internet connection");
            }
    
            return await _client.SendAsync(request, cancellationToken);
        }
    
        private bool CheckInternetConnection()
        {
            var current = Connectivity.NetworkAccess;
            if (current == NetworkAccess.None)
                return false;
            else
                return true;
        }
    }
    

    In UserService I create static CustomHttpClient from which I then call SendAsync method like so:
    public class UserService

        private static CustomHttpClient customHttpClient = new CustomHttpClient();
    
        public async Task<FirstModel> GetFirstPageDataAsync()
        {
    
            FirstModel firstModel = new FirstModel();
            try
            {
                string token = await SecureStorage.GetAsync("auth_token").ConfigureAwait(false);
                var request = new HttpRequestMessage(HttpMethod.Get, $"{BaseAddress}/fistpagedata");
                request.Headers.Accept.Clear();
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
                // this is where i call customHttpClient
                // if there is no connection, TaskCanceledException is thrown
                // which is caught by catch block
                var response = await customHttpClient.SendAsync(request, CancellationToken.None).ConfigureAwait(false);
    
                if (response.IsSuccessStatusCode)
                {
                    ...
                }
    
            }
            catch (Exception e)
            {
    
                throw;
            }
            ...
    

    And then in ViewModel, from which GetFirstPageDataAsync() is called I catch this exception and show Error page. Good thing here is, if any other exception happened, not just no internet connection, ErrorPage will be shown too.

    private async Task LoadData()
    {
        try
        {
            FirstModel firstModel = new FirstModel();
            firstModel = await _userService.GetFirstPageDataAsync().ConfigureAwait(false);
            ...
        }
        catch (Exception e)
        {
            await ShowErrorPage();
        }
    }
    

    I hope I this will help someone with similar problems :)

Answers

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭
    But a generic Exception should catch it
  • igorkr_10igorkr_10 Member ✭✭✭✭
    edited December 2019

    Catch it like this:

    catch (HttpRequestException)
    {
        await App.MainPage.DisplayAlert("Error", "Connection failure", "OK");
    }
    
  • JarvanJarvan Member, Xamarin Team Xamurai
    edited December 2019

    Check if device has connection during app run

    You can create a viewmodel to handle the connection state changes by subscribing to the ConnectivityChanged event.

    public class BaseViewModel : INotifyPropertyChanged
    {
        public bool IsNotConnected { get; set; }
        public BaseViewModel()
        {
            Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
            IsNotConnected = Connectivity.NetworkAccess != NetworkAccess.Internet;
        }
    
        BaseViewModel()
        {
            Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
        }
    
        void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
        {
            IsNotConnected = e.NetworkAccess != NetworkAccess.Internet;
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    Tutorial: https://xamgirl.com/handling-connection-changes-in-xamarin-forms/

  • rozman50rozman50 Member ✭✭

    @JoeManke
    What do you do after you check for connection and there is none?

    If I push NoConnectionPage to the stack then I must prevent back button press, so user can't go back.
    Setting MainPage = NoConnectionPage would be a solution, but takes a lot of resources and Navigation stack is lsot.

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    I basically do what @igorkr_10 suggested. Instead of navigating away, display an alert that says "No network connection" and provide a retry/refresh button on the existing page.

  • rozman50rozman50 Member ✭✭
    Accepted Answer

    With help and suggestions of all you above, I created this. If there is anything that could be improved below, please share :)0

    I created custom HttpClient class, which check for internet connection before any http call is made:

    public class CustomHttpClient : System.Net.Http.HttpClient
    {
        private static HttpClient _client = new HttpClient();
        public CustomHttpClient()
        {
    
        }
    
        public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!CheckInternetConnection())
            {
                throw new TaskCanceledException("No internet connection");
            }
    
            return await _client.SendAsync(request, cancellationToken);
        }
    
        private bool CheckInternetConnection()
        {
            var current = Connectivity.NetworkAccess;
            if (current == NetworkAccess.None)
                return false;
            else
                return true;
        }
    }
    

    In UserService I create static CustomHttpClient from which I then call SendAsync method like so:
    public class UserService

        private static CustomHttpClient customHttpClient = new CustomHttpClient();
    
        public async Task<FirstModel> GetFirstPageDataAsync()
        {
    
            FirstModel firstModel = new FirstModel();
            try
            {
                string token = await SecureStorage.GetAsync("auth_token").ConfigureAwait(false);
                var request = new HttpRequestMessage(HttpMethod.Get, $"{BaseAddress}/fistpagedata");
                request.Headers.Accept.Clear();
                request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
                // this is where i call customHttpClient
                // if there is no connection, TaskCanceledException is thrown
                // which is caught by catch block
                var response = await customHttpClient.SendAsync(request, CancellationToken.None).ConfigureAwait(false);
    
                if (response.IsSuccessStatusCode)
                {
                    ...
                }
    
            }
            catch (Exception e)
            {
    
                throw;
            }
            ...
    

    And then in ViewModel, from which GetFirstPageDataAsync() is called I catch this exception and show Error page. Good thing here is, if any other exception happened, not just no internet connection, ErrorPage will be shown too.

    private async Task LoadData()
    {
        try
        {
            FirstModel firstModel = new FirstModel();
            firstModel = await _userService.GetFirstPageDataAsync().ConfigureAwait(false);
            ...
        }
        catch (Exception e)
        {
            await ShowErrorPage();
        }
    }
    

    I hope I this will help someone with similar problems :)

Sign In or Register to comment.