1
\$\begingroup\$

Quite simply, this is my base class for Xamarin forms, everything is fully working, but it's getting a bit too long and feels a bit like it can be improved, especially the network bit could be handled differently.

Anyone have any idea how I can split this up?

/// <summary> The startup class for the cross-platform project </summary>
    public partial class App
    {
        /// <summary> The container for all our dependency injections.  </summary>
        private static TinyIoCContainer _container;

        /// <summary> Handles pop up and other alerts </summary>
        private readonly IDialogService _dialogService;

        public static Double ScreenHeight { get; set; }
        public static Double ScreenWidth { get; set; }

        /// <summary>
        ///     Initializes a new instance of the <see cref="App" /> class.
        ///     This is the start up method of the entire application along with it's corresponding start up method in the native
        ///     platform.
        /// </summary>
        public App()
        {
            InitializeComponent();

            // Register for connectivity changes, be sure to unsubscribe when finished
            Connectivity.ConnectivityChanged += ConnectivityChanged;

            // Enables Experimental features
            Device.SetFlags(new[] {"Markup_Experimental", "Brush_Experimental", "Shapes_Experimental"});

            // Retrieve token
            var token = GetTokenAsync().Result;
#if DEBUG
            EnableDebugRainbows(false);
            RegisterDependencies(true);
            GlobalSetting.Instance.UpdateRunMode(true, token);
#else
            RegisterDependencies(false);
            GlobalSetting.Instance.UpdateRunMode(true, token);
#endif

            _dialogService = TinyIoCContainer.Current.Resolve<IDialogService>();

            // Start navigation, do this last
            InitNavigation();

            SetDeviceHeightAndWidth();
        }

        private void SetDeviceHeightAndWidth()
        {
            // Get Metrics
            var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;

            // Width (in pixels)
            var width = mainDisplayInfo.Width;
            // Width (in xamarin.forms units)
            var xamarinWidth = width / mainDisplayInfo.Density;
            // Height (in pixels)
            var height = mainDisplayInfo.Height;
            // Screen density
            var density = mainDisplayInfo.Density;

            ScreenWidth = xamarinWidth;
        }

        /// <summary> Called when the application starts. </summary>
        protected override void OnStart()
        {
            // Initialize AppCenter
            AppCenter.Start(
                """,
                typeof(Analytics),
                typeof(Crashes));
        }

        /// <summary> Gets the token from secure storage in app </summary>
        /// <returns> returns token as a string </returns>
        private static async Task<string> GetTokenAsync()
        {
            try
            {
                var oauthToken = await SecureStorage.GetAsync("AuthAccessToken").ConfigureAwait(false);
                return oauthToken;
            }
            catch (Exception exception)
            {
                // Possible that device doesn't support secure storage on device.
                Debug.WriteLine(exception);
                throw new Exception();
            }
        }

        /// <summary> Applies Debug Coloring to UI if enabled </summary>
        private void EnableDebugRainbows(bool shouldUseDebugRainbows)
        {
            Resources.Add(new Style(typeof(ContentPage))
            {
                ApplyToDerivedTypes = true, Setters = {new Setter {Property = DebugRainbow.ShowColorsProperty, Value = shouldUseDebugRainbows}}
            });
        }

        /// <summary> Registers our dependency services. </summary>
        public static void RegisterDependencies(bool inDebugMode)
        {
            // 1. Create a new Tiny Ioc container
            _container = TinyIoCContainer.Current;

            // 2. Register Services - by default, TinyIoC will register interface registrations as singletons.
            _container.Register<IRequestProvider, RequestProvider>();
            _container.Register<ISettingsService, SettingsService>();
            _container.Register<IAuthenticationService, AuthenticationService>();
            _container.Register<IDialogService, DialogService>();

            if (inDebugMode)
            {
                _container.Register<IFormsService, FormsMockService>();
                _container.Register<IJobsService, JobsMockService>();
                _container.Register<IMediaService, MediaFileMockService>();

                GlobalSetting.InDebugMode = true;
            }
            else
            {
                _container.Register<IFormsService, FormService>();
                _container.Register<IJobsService, JobsService>();
                _container.Register<IMediaService, MediaFileService>();

                GlobalSetting.InDebugMode = false;
            }
        }

        /// <summary> Decides where user will be redirected to, if logged in already skip login. </summary>
        private void InitNavigation()
        {
            var navigationHelper = new ShellNavigationHelper();

            var currentAssembly = Assembly.GetExecutingAssembly();
            navigationHelper.RegisterViewsInAssembly(currentAssembly);

            TinyIoCContainer.Current.Register<INavigationHelper>(navigationHelper);

            foreach (var type in currentAssembly.ExportedTypes.Where(x => x.IsSubclassOf(typeof(ViewModelBase))))
            {
                TinyIoCContainer.Current.Register(type);
            }

            foreach (var type in currentAssembly.ExportedTypes.Where(x => x.IsSubclassOf(typeof(Page))))
            {
                TinyIoCContainer.Current.Register(type);
            }

            Resolver.SetResolver(new TinyIoCResolver());

            var token = GlobalSetting.Instance.AuthToken;

            // If no token go to login page, else go to main page
            if (string.IsNullOrEmpty(token))
            {
                MainPage = new LoginPage();
            }
            else
            {
                MainPage = new AppShell();
            }
        }

        /// <summary> Called when the application is resumed, after being sent to the background. </summary>
        protected override void OnResume()
        {
            Connectivity.ConnectivityChanged += ConnectivityChanged;
        }

        /// <summary> Called each time the application goes to the background. </summary>
        protected override void OnSleep()
        {
            Connectivity.ConnectivityChanged -= ConnectivityChanged;
        }

        /// <summary> Show a toast at bottom when connectivity is lost </summary>
        /// <param name="sender">  The sender. </param>
        /// <param name="eventArgs">  The eventArgs hold the connectivity information </param>
        private void ConnectivityChanged(object sender, ConnectivityChangedEventArgs eventArgs)
        {
            var access = eventArgs.NetworkAccess;
            var netStatus = CheckNetAccess(access);

            _dialogService.ShowAlertAsync(netStatus, "Internet", "OK");

            var profiles = eventArgs.ConnectionProfiles;
            var internetType = CheckInternetType(profiles);

            _dialogService.ShowAlertAsync(internetType, "Internet Type", "OK");
        }

        private static string CheckInternetType(IEnumerable<ConnectionProfile> profiles)
        {
            var connectionProfiles = profiles.ToList();
            return connectionProfiles.Contains(ConnectionProfile.WiFi)
                ? connectionProfiles.FirstOrDefault().ToString()
                : "No Wifi";
        }

        private static string CheckNetAccess(NetworkAccess access)
        {
            return access switch
            {
                NetworkAccess.Internet => "Local and internet access",
                NetworkAccess.Local => "Local network access only",
                NetworkAccess.ConstrainedInternet => "Limited internet access",
                NetworkAccess.None => "No connectivity is available",
                NetworkAccess.Unknown => "Unable to determine internet connectivity",
                _ => throw new ArgumentOutOfRangeException(nameof(access), access, null)
            };
        }
    }
}
\$\endgroup\$
3
\$\begingroup\$

Re-entrance

I don't think that these:

        private static TinyIoCContainer _container;
        public static Double ScreenHeight { get; set; }
        public static Double ScreenWidth { get; set; }

should be static. Particularly for the purposes of unit testing, these should simply be instance properties on your App. The feasibility of this might be limited by an external contract for RegisterDependencies that I don't understand, mind you.

Implicit types

var is over-used, here. The usual policy I recommend is that - if a type is obvious, such as the case of an object being constructed and then assigned, var is fine; like this:

var navigationHelper = new ShellNavigationHelper();

But, for something like this:

var netStatus = CheckNetAccess(access);

Can you tell, just by looking at this line, the type of netStatus? No. Part of self-documenting code is making this more legible to others, and to yourself in a few weeks, by adding explicit types. An added benefit is that it will catch a certain category of regressions if the expected type changes.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I was thinking more in line of how to split up the networking code and didn't even consider those improvements, thanks! \$\endgroup\$ – JsonDork Oct 10 at 13:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.