Detect network connection type

Hi,
I have a cross platform app.
I need to know what kind of network (wi-fi or roaming) my app is using. I see there are class/method for Android and iOS, but they are bounded to platform.
There is a way to retrieve this information crossplatform within the same class?

Posts

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭

    If each OS has a different mechanism, then the best way to achieve a solution with only one class is to use a partial class and partial methods.

    I haven't got an example for network detection, but I do have an example on GitHub here, which is described in the associated blog entry. In this example I've used partial classes and methods to initialise my database file depending on the platform.

    What you do is have a partial class with a partial method:

    public partial class TreeNameRepository
    {
        #region Partial Methods
        partial  void InitialiseDatabase ();
        #endregion
        // rest of class definition follows...
    }
    

    Then the two implementations are defined in platform-specific files, included in the platform-specific projects. So for Android my InitialiseDatabase implementation looks like this:

    partial  void InitialiseDatabase()
    {
        string Content;
        StreamReader Reader;
        StreamWriter Writer;
        try
        {
            FullFilePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal),"TreeNames.txt");
            if (!File.Exists(FullFilePath))
            {
                Reader= new StreamReader (Application.Context.Assets.Open("TreeNames.txt"));
                Content = Reader.ReadToEnd ();
                Writer = new StreamWriter(FullFilePath);
                Writer.Write(Content);
                Reader.Close();
                Writer.Close();
            }
        }
        catch (Exception ex)
        {
            throw new Exception(string.Format("Cannot copy TreeNames to {0} {1}",FullFilePath,ex.ToString()));
        }
    }
    

    While for iOS it looks like this:

    partial void InitialiseDatabase()
    {
        FullFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(),@"Files/TreeNames.txt");
    }
    

    So then we can call InitialiseDatabase without worrying about what platform we're on - in my example, in the constructor for my repository:

     private  TreeNameRepository ()
     {
         // Initialise the database - the implementation of which is platform-specific.
         InitialiseDatabase ();
     }
    

    So you could define a partial class called NetworkDetails, with a partial method NetworkType
    which is implemented differently for each platform.

  • ChrisMillerChrisMiller USBeta, Developer Group Leader ✭✭

    An alternate method is to use platform specific compiler variables with #if conditional directives. I prefer the partial class method that James described, but you have that as an option.

  • LucaFongaroLucaFongaro USMember ✭✭

    Thanks for the suggestion. I will use the version with partial class :-)

  • VirgiVirgi BEMember ✭✭
    edited April 2014

    @JamesLavery‌ How do you resolve the fact that your partial classes are spread in different projects ? I've tried to do that, but I keep getting 'No defining declaration found for implementing declaration of partial method'. I've been searching online how to do that, but I only read that's impossible to do.

    EDIT : Nevermind, found the problem 3 minutes after writing this post. Thanks for the example anyway :) was a great help

  • JonDouglasJonDouglas USXamarin Team, University, Developer Group Leader Xamurai

    @Vi‌

    You have a few options:

    1. You can use the File Linking #if conditionals in one class to determine what will be used on each platform.

    2. You can use the abstraction pattern and solve your problem with indirection:

    http://blogs.msdn.com/b/dsplaisted/archive/2012/08/27/how-to-make-portable-class-libraries-work-for-you.aspx - See section "Solving Problems With Indirection"

  • PeterDavisPeterDavis USMember ✭✭✭
    edited April 2014

    Another way to do this is to use an IoC container. You can create a fairly basic IoC container that's little more than a Dictionary<Type, Object> where the Type is your key is the interface type and the value is the object instance (or if you want you can create a resolver that can instantiate the objects from a type.). If you look around on the net, you can find some basic IoC implementations. This is the way we handle a lot of our cross platform stuff. For example, in my PCL, I have an interface called ILogger:

    public interface ILogger
    {
        void Info(string message);
        void Warn(string message);
        void Error(string message);
    }
    

    I then implement it in both the Android and IOS sides. In the Android and IOS clients, when they start up and I bootstrap the IoC container, I have:

            IoC.Container.RegisterServiceType<ILogger, Logger>();
    

    Logger, being the local implementation.

    Then whenever I want a logger (whether it's from within the PCL or from my client), I call IoC.Container.Resolve() and I have the proper one for my environment.

    I use a similar mechanism to obtain the directory for storing our database on the mobile device, or for implementing the network detection code.

    I stole this IoC container: https://github.com/MvxMod/MvxMod/blob/master/Cirrious/Cirrious.MvvmCross/IoC/MvxSimpleIoCContainer.cs

    and then made some modifications to it to support constructor, parameter and field injection.

    Here's my version. Help yourself to it:

    public class IoC
    {
        private readonly Dictionary<Type, IResolver> _resolvers = new Dictionary<Type, IResolver>();
    
        private static IoC _container;
        public static IoC Container
        {
            get
            {
                if (_container == null)
                {
                    _container = new IoC();
                }
                return _container;
            }
        }
    
    
        #region Public Methods
    
        public bool CanResolve<T>()
            where T : class
        {
            lock (this)
            {
                return _resolvers.ContainsKey(typeof(T));
            }
        }
    
        public bool CanResolve(Type type)
        {
            lock (this)
            {
                return _resolvers.ContainsKey(type);
            }
        }
    
        public bool TryResolve<T>(out T resolved)
            where T : class
        {
            lock (this)
            {
                IResolver resolver;
                if (!_resolvers.TryGetValue(typeof(T), out resolver))
                {
                    resolved = default(T);
                    return false;
                }
    
    
                var raw = resolver.Resolve();
                if (!(raw is T))
                {
                    throw new Exception(string.Format("Resolver returned object type {0} which does not support interface {1}",
                                           raw.GetType().FullName, typeof(T).FullName));
                }
                resolved = (T)raw;
                return true;
            }
        }
    
        public bool TryResolve(Type type, out object resolved)
        {
            lock (this)
            {
                IResolver resolver;
                if (!_resolvers.TryGetValue(type, out resolver))
                {
                    resolved = Activator.CreateInstance(type);
                    return false;
                }
                object raw = resolver.Resolve();
                if ((raw.GetType() != type) && !type.IsAssignableFrom(raw.GetType()))
                {
                    throw new Exception(string.Format("Resolver returned object type {0} which does not support interface {1}",
                                           raw.GetType().FullName, type.FullName));
                }
                resolved = raw;
                return true;
            }
        }
    
        public T Resolve<T>()
            where T : class
        {
            lock (this)
            {
                T resolved;
                if (!this.TryResolve(out resolved))
                {
                    throw new Exception(string.Format("Failed to resolve type {0}", typeof(T).FullName));
                }
                return resolved;
            }
        }
    
        public object Resolve(Type type)
        {
            lock (this)
            {
                object resolved;
                if (!this.TryResolve(type, out resolved))
                {
                    throw new Exception(string.Format("Failed to resolve type {0}", type.FullName));
                }
                return resolved;
            }
        }
    
        public void RegisterServiceType<TInterface, TToConstruct>()
            where TInterface : class
            where TToConstruct : class
        {
            lock (this)
            {
                _resolvers[typeof(TInterface)] = new ConstructingResolver(typeof(TToConstruct));
            }
        }
    
        public void RegisterServiceInstance<TInterface>(TInterface theObject)
            where TInterface : class
        {
            lock (this)
            {
                _resolvers[typeof(TInterface)] = new SingletonResolver(theObject);
            }
        }
    
        public void RegisterServiceInstance<TInterface>(Func<TInterface> theConstructor)
            where TInterface : class
        {
            lock (this)
            {
                _resolvers[typeof(TInterface)] = new ConstructingSingletonResolver(() => (object)theConstructor());
            }
        }
    
        #endregion
    
        #region Resolver Helper Classes
    
        private interface IResolver
        {
            object Resolve();
        }
    
        private class BaseResolver : IResolver
        {
    
            public Type ObjectType { get; protected set; }
    
            #region Implementation of IResolver
    
            public virtual object Resolve()
            {
                throw new NotImplementedException("You must override this in the derived class.");
            }
    
            #endregion
    
            #region Protected Methods
    
            protected void PopulateFieldsAndProperties(object instance)
            {
                PropertyInfo[] props = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                FieldInfo[] fields = instance.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    
                foreach (PropertyInfo pi in props)
                {
                    if (_container.CanResolve(pi.PropertyType) && pi.CanWrite && pi.GetValue(instance, null) == null)
                    {
                        pi.SetValue(instance, _container.Resolve(pi.PropertyType), null);
                    }
                }
                foreach (FieldInfo fi in fields)
                {
                    if (_container.CanResolve(fi.FieldType) && !fi.IsInitOnly && fi.GetValue(instance) == null)
                    {
                        fi.SetValue(instance, _container.Resolve(fi.FieldType));
                    }
                }
            }
    
            protected bool RequiresParameters()
            {
                ConstructorInfo[] ci = ObjectType.GetConstructors();
                if (ci.Count() > 0)
                {
                    if (ci.Where(p => p.GetParameters().Length == 0).FirstOrDefault() != null)
                    {
                        return false;
                    }
                }
                return true;
            }
    
            protected object CreateParameterizedInstance()
            {
                ConstructorInfo[] ci = ObjectType.GetConstructors();
                if (ci.Length > 1)
                {
                    throw new ArgumentException("Type " + ObjectType.FullName + " has more than once constructor");
                }
    
                ParameterInfo[] paramInfos = ci[0].GetParameters();
                List<object> paramValues = new List<object>();
                foreach (ParameterInfo pi in paramInfos)
                {
                    if (_container.CanResolve(pi.ParameterType))
                    {
                        paramValues.Add(_container.Resolve(pi.ParameterType));
                    }
                    else
                    {
                        throw new ArgumentException("Unable to resolve type " + pi.ParameterType.FullName + " as parameter for constructor of type " + ObjectType.FullName);
                    }
                }
    
                return Activator.CreateInstance(ObjectType, paramValues.ToArray());
            }
    
            #endregion
        }
    
    
        private class ConstructingResolver : BaseResolver
        {
            public ConstructingResolver(Type type)
            {
                ObjectType = type;
            }
    
    
            #region Implementation of IResolver
    
    
            public override object Resolve()
            {
                object instance = null;
                if (RequiresParameters())
                {
                    instance = CreateParameterizedInstance();
                }
                else
                {
                    instance = Activator.CreateInstance(ObjectType);
                }
                PopulateFieldsAndProperties(instance);
                return instance;
            }
    
            #endregion
    
        }
    
    
        private class SingletonResolver : BaseResolver
        {
            private readonly object _theObject;
    
    
            public SingletonResolver(object theObject)
            {
                _theObject = theObject;
            }
    
    
            #region Implementation of IResolver
    
    
            public override object Resolve()
            {
                return _theObject;
            }
    
            #endregion
        }
    
    
        private class ConstructingSingletonResolver : BaseResolver
        {
            private readonly Func<object> _theConstructor;
            private object _theObject;
    
    
            public ConstructingSingletonResolver(Func<object> theConstructor)
            {
                _theConstructor = theConstructor;
            }
    
    
            #region Implementation of IResolver
    
    
            public override object Resolve()
            {
                if (_theObject != null)
                    return _theObject;
    
    
                lock (_theConstructor)
                {
                    if (_theObject == null)
                    {
                        _theObject = _theConstructor();
                        PopulateFieldsAndProperties(_theObject);
                    }
                }
    
    
                return _theObject;
            }
    
    
            #endregion
        }
    
        #endregion
    }
    
  • Which IoC container are common in the "Xamarin world"? Unity, Func, ...? Don't like to reinvent the wheel.

  • JonDouglasJonDouglas USXamarin Team, University, Developer Group Leader Xamurai

    @hfrmobile

    I would recommend Unity/Ninject/AutoFac or you can check out Rob Gibbens post on this topic:

    http://arteksoftware.com/ioc-containers-with-xamarin/

  • Billy12ShovelsBilly12Shovels USUniversity ✭✭✭

    https://github.com/jamesmontemagno/Xamarin.Plugins/tree/master/Connectivity

    This is a simple way to detect network settings.

Sign In or Register to comment.