Xamarin.Forms ile Katmanlı Mimariye Giriş – 7 (Crash, Exception)

Merhabalar,

Serimizin 6. yazısında uygulamamızın ihtiyaçlarına göre projemizi genişletmeye başlamıştık. Bu yazımızda ise Crash ve Exception yönetimini geliştireceğiz.

Crash

AppCenter Initialize işlemini yaparken, Crash olduğunda loglanması için gerekli işlemi yapmıştık.

Microsoft.AppCenter.AppCenter.Start(_appCenterBuild.ToString(),
              typeof(Analytics), typeof(Crashes), typeof(Distribute));

Bu Init işlemi zaten ihityacımızı karşılayacak fakat Crashes sınıfınin detaylarini linkteki dökümanı incelemenizi öneririm.

Exception

Exception yönetimini iki şekilde işlememiz gerekiyor.

  • API Exception
  • Custom Exception
            try
            {
                var _api = _apiRequestSelector.GetApiRequestByPriority(_request, priorityType);
                _task = _api.Get();
                result = await Policy
                          .Handle<ApiException>()
                          .WaitAndRetryAsync(retryCount: 2, sleepDurationProvider: retryAttempt =>
                          TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
                          .ExecuteAsync(async () => await _task);
            }
            catch (ApiException apiException)
            {
                exception = apiException;
            }
            catch (Exception ex)
            {
                exception = ex;
            }

Kodlarımızı incelediğimizde servis methodlarımızdaki Exception yönetiminin sürekli try catch blokları içerisinde olduğunu görüyoruz. Özetle her yeni servis methodu geliştirdiğimde, içerisindeki her method için ben bu blokları açıp, tek tek geliştirmeleri yapmak zorunda olacağım. Bu şekilde bir geliştirme mantığıyla servislerimizdeki en ufak bir değişiklikte ne kadar method varsa refactor ihtiyacı doğacaktır. Genel anlamda da servis olarak kullandığımız sınıflarımız içerisinde Exception yönetimini bu şekilde kullanmaktan kaçınmalıyız. Çünkü Exception yönetimini, tükettiğimiz servis katmanlarında hatalı bir şekilde yapmamız, hatanın kaynağını kaybetmemize ve doğru yönetememize neden olabilir. Bununla birlikte Polly, Refit gibi yardımcı arkadaşları kullanma amacımız, bu tarz ihtiyaçları ufak middleware geliştirmeleri yaparak tek bir yerden yönetmek değil mi?

AppCenterService üzerinde geliştirmelere başlayabiliriz.

IAppCenterService

    public interface IAppCenterService : IServiceBase
    {
        void Init();
        bool HandleApiException(ApiException ex);
        void HandleException(Exception ex);
    }

AppCenterService

    public class AppCenterService : IAppCenterService
    {
        public bool HandleApiException(ApiException ex)
        {
            Crashes.TrackError(ex, new Dictionary<string, string>
            {
                { "Time", DateTime.Now.ToString() },
                { "Exception-Type", "API" }
            });
            return false;
        }

        public void HandleException(Exception ex)
        {
            Crashes.TrackError(ex, new Dictionary<string, string>
            {
                { "Time", DateTime.Now.ToString() },
                { "Exception-Type", "CUSTOM" }
            });
        }

        public void Init()
        {
            StringBuilder _appCenterBuild = new StringBuilder();
            if (!String.IsNullOrEmpty(GlobalSetting.Instance.AppCenterAndroidKey))
                _appCenterBuild.Append($"android={GlobalSetting.Instance.AppCenterAndroidKey}");
            if (!String.IsNullOrEmpty(GlobalSetting.Instance.AppCenteriOSKey))
                _appCenterBuild.Append($"ios={GlobalSetting.Instance.AppCenteriOSKey}");
            if (!String.IsNullOrEmpty(GlobalSetting.Instance.AppCenterUWPKey))
                _appCenterBuild.Append($"uwp={GlobalSetting.Instance.AppCenterUWPKey}");

            if (String.IsNullOrEmpty(_appCenterBuild.ToString()))
                throw new Exception("You have to set AppCenter secret key");

            if (!GlobalSetting.Instance.IsTest)
                Distribute.CheckForUpdate();

            Microsoft.AppCenter.AppCenter.Start(_appCenterBuild.ToString(),
              typeof(Analytics), typeof(Crashes), typeof(Distribute));
        }
    }

Yeni eklediğimiz HandleException ve HandleApiException methodları, hataları AppCenter üzerinden yönetmemizi sağlayacak. Eğer api call yaparken bir hata aldıysak ve ilk hatada işlemi sonlandırmak istiyorsak HandleApiException methodu false dönmeli. Aksi halde WaitAndRetryAsync methoduna verdiğimiz retryCount kadar api call yapılıp, eğer her call’da hata alınırsa HandleApiException methodumuz çalışacaktır. Özetle buradaki return bool değerini retry etme isteğinize göre kullanabilirsiniz.

Servislerimizdeki geliştirmelere başlayabiliriz.

UserService

    public class UserService : IUserService
    {
        private readonly IApiRequest<IUserEndpoint> _request;
        private readonly IApiRequestSelector<IUserEndpoint> _apiRequestSelector;
        private readonly IAppCenterService _appCenterService;
        public UserService(IApiRequest<IUserEndpoint> request,
            IApiRequestSelector<IUserEndpoint> apiRequestSelector,
            IAppCenterService appCenterService)
        {
            _request = request;
            _request.BaseApiAddress = "https://reqres.in/api404";
            _apiRequestSelector = apiRequestSelector;
            _appCenterService = appCenterService;
        }
        public async Task<List<Models.User>> Get(PriorityType priorityType)
        {
            List<Models.User> result = null;
            Task<List<Models.User>> _task = null;

            var _api = _apiRequestSelector.GetApiRequestByPriority(_request, priorityType);
            _task = _api.Get();

            result = await Policy
              .Handle<ApiException>(_appCenterService.HandleApiException)
              .WaitAndRetryAsync(retryCount: 2, sleepDurationProvider: retryAttempt =>
              TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))
              .ExecuteAsync(async () => await _task);

            return result;
        }
    }

Öncelikle try catch bloklarını kaldırdık ve Handle methodunun overload halini kullandık.

public static PolicyBuilder Handle<TException>(Func<TException, bool> exceptionPredicate) where TException : Exception;

AppCenter portalden Diagnostics kısmına gittiğimizde Issues tabının altında Errors sekmesini göreceğiz.

Hatanın detayına girip tüm ayrıntıları inceleyebilirsiniz.

Custom Exception yönetimini de aşağıdaki şekilde yapabilirsiniz.

            try
            {
                throw new Exception("Test Exception");
            }
            catch (Exception ex)
            {
                _appCenterService.HandleException(ex);
            }

Görüşmek üzere

Commit detayları : https://github.com/ozaksuty/MyXF/commit/5198ad4413c5d54dd7caa813843e7bb252658b06

Yiğit

Xamarin Developer, Consultant & Architect. Community Leader and Director of Xamarin Türkiye

Post A Reply