"اتصال به اینترنت برقرار نیست! تلاش مجدد" یک استراتژی کلی

اینترنت متصل نیست! تلاش مجدد
اینترنت متصل نیست! تلاش مجدد


در این مقاله قصد دارم یک راه حل کلی به جهت پیاده سازی روشی برای ارسال مجدد ریکوئست های fail شده بخاطر عدم اتصال به اینترنت ارائه بدم

من قصد دارم اینجا از RxSwift و Moya استفاده کنم (چرا که کار رو خیلی راحت تر کردن) تا ایده اصلی رو به شما منتقل کنم و بعد شما میتونید به هر نحوی که دوست داشتین پیاده سازیش کنید.




  • ماجرا چیه؟

ماجرا از این قراره که ما توی خیلی از برنامه هایی که می نویسیم ریکوئست هایی رو به سرور ارسال کنیم و ممکنه این ریکوئست ها بخاطر عدم اتصال به اینترنت fail بشن.

در این جور مواقع باید پیغامی رو با متن و ظاهری مناسب به کاربر نمایش بدیم و مشکل رو بهش یادآوری کنیم و در ادامه هم امکان تلاش مجدد و یا نادیده گرفتن این پیغام رو بهش بدیم.

اما چطوری باید همه ریکوئست هایی که توی اون لحظه ارسال شدن و صرفا بخاطر عدم اتصال به اینترنت fail شدن رو دوباره ارسال کنیم؟ اینجاست که  RxSwift و  operator های خفنش وارد میشن


در زیر بهتره یک نگاه کلی نسبت به کاری که قراره انجام بدیم داشته باشیم:

لایه presentation و network یک رفرنس از آبجکتی از جنس GenericNetworkErrorRetryStrategy رو دارن. در واقع این دو لایه به واسطه یک آبجکت مشترک از کلاسی که GenericNetworkErrorRetryStrategy رو پیاده سازی کرده باهم تعامل دارن


با کمک moya یک لایه ساده برای api هامون ایجاد کردیم که خیلی سادست و نیاز به توضیحی نداره:

https://gist.github.com/matinouf/73240c8846fe60821d979cb68f3850f7



استراتژی ما

اینجا یک protocol به اسم GenericNetworkErrorRetryStrategy داریم که در ادامه پیاده سازیش می‌کنیم اما فعلا فقط قراره بدونیم کاربرد پراپرتی ها و متد هاش چیه:

https://gist.github.com/matinouf/e16fa6c7948cccb6d0e31f7bcaedbf9a

۱) این Observable یک مقدار از جنس boolean رو emit میکنه و قراره در لایه network ازش استفاده بشه. یک مثال ساده: زمانی که کاربر روی دکمه تلاش مجدد (در لایه presentation) کلیک می‌کنه این observable مقدار true رو emit میکنه و در زمانی که دکمه cancel کلیک میشه مقدار false رو.

۲) از این Observable در لایه presentation استفاده میشه و زمانی که یک ریکوئست fail میشه یکی از case های تعریف شده در Api رو emit میکنه. تا بدونیم به طور دقیق کدوم یکی از ریکوئست ها ناموفق بوده(توی این مقاله از این مورد استفاده نمی‌کنیم).

۳) همونطور که از اسمش مشخصه وضعیت نمایش پیامی مبنی بر "عدم اتصال به اینترنت" رو emit میکنه و در لایه presentation قراره بهش subscribe انجام بشه.

۴) این متد در لایه Network مورد استفاده قرار میگیره و زمانی که ریکوئستی fail بشه و دلیلش عدم اتصال به اینترنت باشه بلافاصله صدا زده میشه.

۵ و ۶) این دو متد در لایه presentation مورد استفاده قرار میگیرن و زمانی که کاربر روی یکی از دکمه های retry یا cancel کلیک کنه صدا زده میشن


خب حالا که با این protocol آشنا شدیم بریم به ساده ترین نوع ممکن پیاده سازیش کنیم:

https://gist.github.com/matinouf/7ea3f1c31f3988b7fd8e0ee8db566afe

به کمک کلاس PublishRelay در فریم وورک RxCocoa پراپرتی ها رو مقدار دهی کردیم.

بعد از اینکه متد ها رو مرور کردید میریم تا ببینیم لایه های network و presentation چطوری قراره از این کلاس استفاده کنن تا بتونن باهم در تعامل باشن.


  • لایه Presentation

ما یک کلاسی فرضی به نام Application رو در نظر گرفتیم (این میتونه AppDelegate برنامه شما باشه) که در لایه presentation قرار داره و قراره که پیغام عدم اتصال به اینترنت رو به کاربر نمایش بده و همچنین اکشن های retry و cancel رو به واسطه رفرنسی که از آبجکتی از جنس GenericNetworkErrorRetryStrategy داره به لایه Network منتقل کنه.

https://gist.github.com/matinouf/b19fb148f991d83346804004d6458373
توجه کنید که ما تنها یک instance از کلاس GenericNetworkErrorRetryStrategyImpl ایجاد می‌کنیم و در لایه های presentation و network اینجکت میکنیم.


  • لایه Network

در زیر یک پیاده سازی ساده از پروتکلی که بالاتر تحت عنوان Networking معرفی کردیم رو میبینیم و خواهیم دید این کلاس چطوری از GenericNetworkErrorRetryStrategy استفاده خواهد کرد:

https://gist.github.com/matinouf/9513a2e7a4a6d326463d9624c757f5a2

همچنین میتونید همین پیاده سازی رو در یک Plugin/Interceptor (که سر راه ریکوئست ها قرار میگیره) انجام بدید.

۱) در اینجا بررسی میکنیم تا اگر error از نوع عدم اتصال به اینترنت بود درخواست نمایش پیغام مناسب رو با صدا زدن متد makeNetworkErrorVisible ایجاد کنیم تا در لایه presentation نمایش این پیغام انجام شود.

۲) با استفاده از یکی از operator های مهم در RxSwift تحت عنوان retryWhen خواهیم توانست اکشنی را که کاربر در لایه presentation انجام میدهد عملیاتی کنیم و در صورتی که درخواست  تلاش مجدد ارسال شد اصطلاحا resubscribe انجام شود تا ریکوئست fail شده مجددا ارسال شود.

همچنین میتونید در کلاس GenericNetworkErrorRetryStrategyImpl وضعیت اتصال به اینترنت رو همواره بررسی کنید و هر موقع اتصال برقرار شد متد retry رو صدا کنید تا اگر ریکوئستی fail شده به طور خودکار مجددا ارسال بشه.



نتیجه گیری

بجای اینکه توی قسمت های مختلف در لایه presentation درگیر هندل کردن مشکلات این چنینی و نوشتن کد های تکراری باشیم میتونیم با پیاده سازی همچین روش هایی هندل کردن این مشکل رو به یک کلاس مجزا بسپاریم تا viewModel ها یا Controller ها درگیری با این مسئله نداشته باشن و روی تسک های خودشون تمرکز کنن.


امیدوارم تونسته باشم چیزی که باید رو به شما هم منتقل کنم. اگر جایی رو متوجه نشدید و واستون مبهم بود حتما کامنت کنید. شایم من بد نوشته باشم یا منظورم رو به بهترین شکل منتقل نکرده باشم.


در نهایت این الگو رو میتونید کامل تر و شخصی سازی تر کنید و ارور های دیگه ای مثل 401,403, 500 ,.... رو هندل کنید.