0

Simple iOS application HTTP communication using DelegatingHandler Improvement of surrounding environment

In today's native client application, I think that it is not an overstatement to say that there is no one that does not perform HTTP communication, I think that is the basis of application development basics. I'm pretty sure that we will feel that we want to intersperse various processing before and after HTTP communication as we develop. For example the following story :

  1. I want to log the contents of the request
  2. I want to prevent requests from running when the wireless connection is bad
  3. I want to run network activity indicator during communication

In such case, System.Net.Http.DelegatingHandler is a very useful substitute. The official document is here: https://msdn.microsoft.com/en-US/library/system.net.http.delegatinghandler(v=vs.110).aspx

1.Output log of HTTP request

For example, if you wanted to log the contents of an Http request, you could create a simple DelegatingHandler subclass like this:

using System.Net.Http;
using System;
using System.Threading.Tasks;

namespace Pawotter.Net.Http
{
    public class HttpNetworkActivityDelegatingHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            Console.WriteLine(request);
            return base.SendAsync(request, cancellationToken);
        }
    }
}

For practical use it may be better to place Console.WriteLine in the critical section, but for the time being it was possible to handle the processing before the request with such a simple code. Simply pass this to the constructor of System.Net.Http.HttpClient. This logs the contents of all requests.

var httpClient = new HttpClient(new HttpNetworkActivityDelegatingHandler());
var res = await httpClient.GetAsync("http://gochiusa.com");

2.Can not send request while being outside service area

The problem becomes easy if we use Reachability

using System.Net.Http;
using System;
using System.Threading.Tasks;
using SystemConfiguration;

namespace Pawotter.Net.Http
{
    public class HttpNetworkActivityDelegatingHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            var reachability = new NetworkReachability(request.RequestUri.AbsoluteUri);
            if (reachability.TryGetFlags(out var flags) && !flags.HasFlag(NetworkReachabilityFlags.Reachable))
            {
                throw new Exception();
            }
            return base.SendAsync(request, cancellationToken);
        }
    }
}

3. Turn the network activity indicator during communication

Management of the network activity indicator, it is very troublesome. It is Zara that communication runs at the same time, so I think that it is necessary to consider that problem. I think that everybody will do the same kind of implementation in general, but if the number of communications currently running is positive, I think that it would be to create counters like stop the indicator when it is turned to 0 I will. Respectively we will step by step and look at the implementation.

3.1. Abstraction of NetworkActivityIndicator

Basically, the Network Activity Manager should have enough ON / OFF interfaces. It is a method named Activate(), Inactivate() respectively. The implementation class receives a UIApplication instance from the outside and tells that it is good to put true and false in the value of NetworkActivityIndicatorVisible at the timing of Activate(), Inactivate().

namespace Pawotter.NetworkActivityManager
{
    interface IIndicator
    {
        void Activate();
        void Inactivate();
    }

    public sealed class NetworkActivityIndicator : IIndicator
    {
        readonly UIApplication application;

        internal NetworkActivityIndicator(UIApplication application)
        {
            this.application = application;
        }

        void IIndicator.Activate() => application.NetworkActivityIndicatorVisible = true;
        void IIndicator.Inactivate() => application.NetworkActivityIndicatorVisible = false;
    }
}

3.2. Create Class to count the request which is being process

If there is more than one request running simultaneously, call IIndicator 's Activate() and let' s make a simple counter class calling Inactivate() when communication is not running.

This counter class will have a simple interface that calls Attach() at the beginning of the communication and Detach() at the end. I think that the interface and implementation code will be as follows.

using System;
using System.Threading;

namespace Pawotter.NetworkActivityManager
{
    public interface IActivityManager
    {
        void Attach();
        void Detach();
    }

    /// <summary>
    /// This class Activates the Indicator when an activity exists (takes a positive value).
    /// Inactivate the Indicator when there is no activity (takes 0).
    /// </summary>
    sealed class AnyActivityIndicatorManager : IActivityManager
    {
        /// <summary>
        /// Number of activities running concurrently
        /// </summary>
        int count;

        readonly IIndicator indicator;
        readonly Object thisObject = new Object();

        internal AnyActivityIndicatorManager(IIndicator indicator)
        {
            this.indicator = indicator;
        }

        /// <summary>
        /// Activate the indicator when the activity count changes from 0 to 1
        /// </summary>
        void IActivityManager.Attach()
        {
            lock (thisObject)
            {
                if (count == 0) indicator.Activate();
                Interlocked.Increment(ref count);
            }
        }

        /// <summary>
        /// Inactivate the indicator when the activity count changes from 1 to 0
        /// </summary>
        void IActivityManager.Detach()
        {
            lock (thisObject)
            {
                if (count == 1) indicator.Inactivate();
                if (count > 0) Interlocked.Decrement(ref count);
            }
        }
    }
}

3.3. Implementing DelegatingHandler

Now that you are ready, let's implement a DelegatingHandler that turns the network activity indicator around when the communication is running, using what you have made so far. It goes as follows.

using System.Net.Http;

namespace Pawotter.NetworkActivityManager
{
    public class HttpNetworkActivityDelegatingHandler : DelegatingHandler
    {
        protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            NetworkActivityManager.Instance.Attach();
            try
            {
                return await base.SendAsync(request, cancellationToken);
            }
            finally
            {
                NetworkActivityManager.Instance.Detach();
            }
        }
    }
}

For those who want to use because the whole code is released in GitHub. It is MIT license. How to release packages that depend on Xamarin.iOS to nuget I do not understand well so I would be happy if you are an expert,

3.4. How to implement with Swift

You can do it by wrapping URLSession and making HttpClient yourself. Implementation is roughly the same. It is a code that has room for improvement both in terms of implementation and performance, but if you are trying to communicate it clearly, you will feel like the following implementation code.

public final class ActivityIndicatorManager {

   private var count: UInt32
   private let application: UIApplication
   private let lock: NSRecursiveLock = NSRecursiveLock()

   public init(application: UIApplication) {
       self.application = application
       count = 0
   }

   public func activate() {
       lock.lock(); defer { lock.unlock() }
       if (count == 0) {
           application.isNetworkActivityIndicatorVisible = true
       }
       count = count + 1
   }

   public func inactivate() {
       lock.lock(); defer { lock.unlock() }
       if (count == 1) {
           application.isNetworkActivityIndicatorVisible = false
       }
       count = count - 1
   }
}

5. Conclusion

Delegating Handler is so convenient that you can use it for multiple purpose, hope you can ultilize it in further project, enjoy !


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí