<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های متین عبداللهی</title>
        <link>https://virgool.io/feed/@matin_ab</link>
        <description>iOS App Developer</description>
        <language>fa</language>
        <pubDate>2026-06-17 12:36:56</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/46593/avatar/2lLS12.png?height=120&amp;width=120</url>
            <title>متین عبداللهی</title>
            <link>https://virgool.io/@matin_ab</link>
        </image>

                    <item>
                <title>&quot;اتصال به اینترنت برقرار نیست! تلاش مجدد&quot; یک استراتژی کلی</title>
                <link>https://virgool.io/MobileLab/%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A8%D9%87-%D8%A7%DB%8C%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D8%A8%D8%B1%D9%82%D8%B1%D8%A7%D8%B1-%D9%86%DB%8C%D8%B3%D8%AA-%D8%AA%D9%84%D8%A7%D8%B4-%D9%85%D8%AC%D8%AF%D8%AF-%DB%8C%DA%A9-%D8%B1%D8%A7%D9%87-%D8%AD%D9%84-%DA%A9%D9%84%DB%8C-gqze3pc7zcnc</link>
                <description>اینترنت متصل نیست! تلاش مجدددر این مقاله قصد دارم یک راه حل کلی به جهت  پیاده سازی روشی برای ارسال مجدد ریکوئست های 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 میکنه. تا بدونیم به طور دقیق کدوم یکی از ریکوئست ها ناموفق بوده(توی این مقاله از این مورد استفاده نمی‌کنیم).۳) همونطور که از اسمش مشخصه وضعیت نمایش پیامی مبنی بر &quot;عدم اتصال به اینترنت&quot; رو 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 ,.... رو هندل کنید.</description>
                <category>متین عبداللهی</category>
                <author>متین عبداللهی</author>
                <pubDate>Thu, 29 Oct 2020 14:52:54 +0330</pubDate>
            </item>
                    <item>
                <title>GCD, Multi-Threading, DispatchQueue,DispatchGroup</title>
                <link>https://virgool.io/@matin_ab/gcd-grand-central-dispatch-swift-5-programming%DB%8C%D8%B3-rflugwbqup9a</link>
                <description>Grand Central Dispatchتوی این مقاله قصد دارم تا درباره multi-threading در Swift بنویسم و شما رو با GCD و کلاس های مهمی مثل DispatchQueue و DispatchGroup آشنا کنم.قبل از همه چیقبل از اینکه بخواییم درباره GCD حرف بزنیم باید یه درک قابل قبولی از threading و concurrency در برنامه نویسی داشته باشیم. پس اگر فکر می‌کنید که در این‌ باره به اندازه کافی میدونید، میتونید این مرحله رو رد کنید.بریم سراغ Threading &amp; Concurrency توی iOS هر برنامه ای که اجرا می‌کنید از یک یا چند thread تشکیل شده. اینکه thread ها چطوری و چه زمان باید اجرا بشن، توی پایین ترین لایه، کاملا به عهده سیستم عامل دستگاه می‌باشد.دیوایس هایی که تنها یک هسته دارن (single-core devices)، برای انجامِ دو یا چند کار به صورت هم زمان از روشی به اسم time-slicing استفاده می‌کنن. حالا این روش چطور کار میکنه؟! فرض کنید دوتا thread داریم که می‌خواییم به صورت هم زمان، هر کدومشون یه کاری رو برای ما انجام بدن. توی این روش پردازنده خیلی سریع بین thread ها switch می‌کنه و مقداری از کار هر thread رو انجام میده. به همین ترتیب کار هر دو thread رو همزمان باهم پیش میبره. تصویر زیر نشون میده این کار چطور انجام میشه:single-core devices از طرف دیگه دیوایس های چند هسته ای (multi-core devices) رو داریم که میتونن thread ها رو به صورت هم زمان و موازی باهم (parallelism) به اجرا در بیارن.multi-core devicesاصل مطلب - GCD و DispatchQueueسه حرف GCD مخفف Grand Central Dispatch هستن. GCD یک کلاس یا یک متد نیست! بلکه یک Low Level API برای مدیریت task ها بصورت همزمان است تا از freez شدن اپلیکیشن جلوگیری کنه. تعریف اپل از GCD:&quot;GCD provides and manages FIFO queues to which your application can  submit tasks in the form of block objects. Work submitted to dispatch  queues are executed on a pool of threads fully managed by the system. No  guarantee is made as to the thread on which a task executes.&quot; این سه کلمه ترجمه تحت الفظیشون به زبان فارسی معنای واضح و قابل درکی نداره. اما شاید اگر بدونید چرا اپل این اسم رو انتخاب کرده، راحت تر بتونید درکش کنید.در واقع GCD اشاره میکنه به یک پایانه راه آهن مسافربری در شهر نیویورک، که به اسم Grand Central Terminal شناخته میشه. GCD به کمک کلاس قدرتمند  DispatchQueue - که در ادامه بیشتر باهاش آشنا میشیم - عملکرد مشابه به یکی از کارهای اصلی پایانه مسافربری، یعنی اعزام قطارها داره.بیایین فرض کنیم هر یک از task ها نقش قطار و هر یک از queueها (صف ها) نقش ریل ها رو بازی می‌کنن. با استفاده از کلاس DispatchQueue تعیین میکنیم هر قطار چه زمانی و از کدوم ریل اعزام بشه. بعضی از ریل ها میتونن چند قطار رو همزمان باهم از خودشون عبور بدن و بعضی از ریل ها فقط یک قطار رو در هر لحظه از خودشون عبور میدن و شرط عبور قطار بعدی از این نوع ریل ها اینه که قطار قبلی به مقصد رسیده باشه. ریلی که همزمان چند قطار رو از خودش عبور میده در واقع شبیه به صفی است که میتونه همزمان چند task رو اجرا کنه. و ریلی که فقط یک قطار رو در هر لحظه از خودش عبور میده درواقع مانند صفی است که در هر لحظه فقط یک task رو میتونه اجرا کنه و شرط اجرای task بعدی در این صف، به پایان رسیدن task قبلیست.بنابرین کلاس DispatchQueue که به فارسی به معنی صف اعزام است، میتونه صف تشکیل بده و task هایی رو به دو طریق sync یا async بهش اعزام کنه. همچنین می‌تونیم بجای استفاده از closure در متد های sync و async، آبجکتی از کلاس DispatchWorkItem به این دو متد پاس بدیم.DispatchQueueانواع DiaptchQueueکلاس DispatchQueue یکی از کلاس های تعریف شده در GCD است که با استفاده از اون میشه به سادگی task هایی رو به صورت همزمان انجام داد. این کلاس صف هایی مستقل از هم تشکیل میده که هر صف وظیفه اجرای task هایی که بهش اعزام میشن رو بر عهده داره. حالا اینکه task های اعزام شده به صف، به صورت متوالی (serial) یا هم‌زمان (concurrent) اجرا بشن، به نوع صفی که تعریف کردیم بستگی داره.انواع صف ها:نوع۱ - Concurrent Queue: در این نوع صف یک یا چند task به صورت همزمان اجرا می‌شن. ترتیب اجرای task ها بر اساس قاعده FIFO می‌باشد. به این معنی که هر task که زود تر اعزام شده باشه، زود تر اجرا می‌شه. اینکه در هر صف دقیقا چه تعداد task می‌تونه اجرا بشه متغیر است و به شرایط و منابع سیستم و وضعیت اپلیکیشن بستگی داره.نوع۲ - Serial Queue: در این نوع صف تضمین داده میشه که در هر زمان فقط و فقط یک task اجرا می‌شه و task ها به صورت متوالی، یکی پس از دیگری اجرا میشن. ترتیب اجرای تسک ها در این نوع صف بر اساس قاعده FIFO می‌باشد. یکی از کاربرد های این نوع صف برای مواقعی است که میخواهید به یک ریسورس مشترک دسترسی داشته باشید تا از تغییر همزمان آن توسط  thread های مختلف و بوجود اومدن مشکل race condition جلوگیری کنید.دقت کنید که شما می‌تونید در هر زمان فقط یک task در هر serial queue اجرا کنید. اما اگر برای مثال چهار صف از نوع serial queue ایجاد کنید و به هر صف یک task اعزام کنید، هر صف می‌تونه به طور مستقل task خودش رو اجرا کنه. در واقع شما دارید چهار task رو در چهار صف مستقل، به طور همزمان اجرا می‌کنید.نوع۳ - Main Queue: این صف نوع جدیدی از صف‌ها نیست بلکه از نوع  Serial Queue می‌باشد. فقط به خاطر اهمیتی که داره در این دسته بندی آورده شده. درباره این صف لازمه بدونیم که تنها یک instance ازش برای هر اپلیکیشن ساخته میشه و در سرتاسر برنامه قابل دسترسیه و تمامی task هایی که بهش اعزام میشه رو در main thread یا به قولی همون UI thread اجرا میکنه. به طور کلی هر چیزی که مربوط به UI هست باید توی این صف به اجرا در بیاد.چند نکته که درباره Dispatch Queue ها باید به خاطر داشته باشید:نکته اول: Dispatch Queue ها task هاشون رو موازی با سایر Dispatch Queue ها اجرا می‌کنن و از هم مستقل هستن.نکته دوم: سیستم، تعداد کل کارهایی که در هر زمان انجام میشن رو تعیین می کنه.نکته سوم: سیستم برای این که تصمیم بیگیره کدوم task ها رو باید بیشتر مورد توجه قرار بده و سریع تر به اجرا درشون بیاره، الویت صف رو در نظر میگیرهنکته چهارم: task ها به محضی که به صف اضافه میشن باید آماده اجرا شدن باشنساخت DispatchQueueقبل از اینکه بخواییم یک task رو به یک صف اعزام کنیم باید بدونیم به چه نوع صفی نیاز داریم. همونطور که بالاتر دیدیم، صف ها میتونن task ها رو به صورت موازی یا متوالی اجرا کنن. الویت هاوقتی یک صف تشکیل میدیم (چه از نوع serial و چه از نوع concurrent) میتونیم با مقدار دادن به پراپرتی qos که مخفف quality of service هست، اهمیت اجرای صف‌ رو برای سیستم مشخص کنیم. ترتیب الویت ها از زیاد به کم:.userInteractive
.userInitiated
.default
.utility
.background
.unspecifiedتوجه: توی بکار گیری الویت ها دقت کنید و سعی کنید از دادن الویت‌های یکسان به صف ها خودداری کنید چرا که در غیر این صورت الویت دادن بی معنی میشه!صف موازی یا هم‌زمان - Concurrent Queueاین صف برای مواقعی مناسبه که قصد داریم task ها رو برای اجرا شدن به صورت هم زمان، بهش اعزام کنیم. هر task که زود تر اعزام بشه زودتر اجرا می‌شه (قاعده FIFO).Concurrent Queueما می‌تونیم به راحتی یک صف از نوع موازی رو با استفاده از متد استاتیک global(qos:) در کلاس DispatchQeueu با سطح الویت هایی که در بالا بهشون اشاره شد، ایجاد کنیم(مقدار qos به صورت پیشفرض برابر با default می‌باشد)Concurrent Dispatchدر پایین، خروجی کد بالا رو میبینیم که task ها هم زمان با هم در یک صف از نوع concurrent اجرا شدن:Concurrent Queue X  0
Concurrent Queue Y  5
Concurrent Queue Z  10
Concurrent Queue Y  6
Concurrent Queue X  1
Concurrent Queue Y  7
Concurrent Queue Y  8
Concurrent Queue Y  9
Concurrent Queue X  2
Concurrent Queue X  3
Concurrent Queue X  4
Concurrent Queue Z  11
Concurrent Queue Z  12
Concurrent Queue Z  13
Concurrent Queue Z  14حالا بیایید دوتا صف با الویت های متفاوت ایجاد کنیم:Concurrent Queues With Different Prioritiesهمونطور که توی خروجی پایین میبینید، صفی که الویت بالاتری داره، بیشتر مورد توجه سیستم قرار میگیره و با الویت بالاتری نسبت به صف های دیگه اجرا میشه:High Priority Concurrent Queue  0
Low Priority Concurrent Queue  5
High Priority Concurrent Queue  1
High Priority Concurrent Queue  2
High Priority Concurrent Queue  3
High Priority Concurrent Queue  4
Low Priority Concurrent Queue  6
Low Priority Concurrent Queue  7
Low Priority Concurrent Queue  8
Low Priority Concurrent Queue  9نکته: هنگام اعزام task ها به یک صفِ موازی، می‌تونید پراپرتی qos رو برای هر task مقدار دهی کنید تا برای صف مشخص بشه موقع اجرای همزمان task ها باهم، به کدوم task اهمیت بیشتری بده.DispatchQueue.global(qos: .background).async(qos: .userInteractive) { // ... }صف متوالی - Serial Queueاز این صف در مواقعی استفاده کنید که نیاز دارید task ها به ترتیب و یکی پس از دیگری اجرا بشن. توی این نوع صف تضمینی وجود داره که میگه همیشه در یک زمان فقط یک task در صف در حال اجراست. همچنین برای مواقعی که قصد دارید از یک ریسورسِ مشترک در برابر race condition محافظت کنید، به جای استفاده از DispatchSemaphore و lock کردن ریسورس، می‌تونید از این نوع صف استفاده کنید.Serial Queueبرای ایجاد صفِ متوالی، کافیه سازنده کلاس DispatchQueue رو فرواخوانی کنید و به پراپرتی label مقدار دلخواه بدید:(مقدار qos به صورت پیشفرض برابر با default می‌باشد)Serial Queueدر خروجی میبینیم که task اول به طور کامل انجام شد و بعد از اون task دوم به اجرا در اومد:First Task  0
First Task  1
First Task  2
First Task  3
First Task  4
Second Task  5
Second Task  6
Second Task  7
Second Task  8
Second Task  9انجام task در Main Queueبرای اینکه یک task در main thread اجرا بشه کافیه اون رو به Main Queue اعزام کنیم:Main Threadتفاوت sync و asyncفرض کنید داخل یک thread (مثلا currentThread) هستید و قصد دارید اجرای یک task رو به یک thread دیگه (مثلا backgroundThread) بسپارید. در این موقع می‌تونید از متد sync یا async استفاده کنید. اما این دو متد یک تفاوت خیلی اساسی دارن. وقتی شما داخل currentThread هستید و متد async رو برای اجرای task در backgroundThread انتخاب می‌کنید، در واقع منتظر تموم شدن task در backgroundThread نمی‌مونید و currentThread به کارش ادامه میده. اما وقتی sync رو صدا می‌زنید، currentThread تا زمان پایان یافتن task در backgroundThread، منتظر باقی می‌مونه.مراقب dead lock باشید!Dead Lockدر کد بالا serialQueue1 و serialQueue2 هر کدوم یک task رو به صورت async اجرا می‌کنن. اما قبل از اینکه task هر کدوم به طور کامل به پایان برسه، هر کدومشون یک task دیگه رو به صورت sync به اون یکی صف اعزام می‌کنه و منتظر میمونه که کار taskای که اعزام کرده به پایان برسه. خب بدیهیه که task هایی که به صورت sync به صف ها اعزام شدن مادامی که صف ها هنوز کار ناتمام دارن، اجرا نمیشن. بنابرین در چنین شرایطی thread های این صف ها به بن‌بست میخورن و اصطلاحا dead lock رخ میده. خروجی این کد رو در پایین میبینیم:serialQeueu1: 0
serialQueue2: 5
serialQueue2: 6
serialQueue2: 7
serialQeueu1: 1
serialQueue2: 8
serialQeueu1: 2
serialQueue2: 9
serialQeueu1: 3
serialQeueu1: 4آخرین مطلب - DispatchGroupگاهی لازم داریم از اتمام کار تمام task هایی که به صورت موازی باهم اجرا کردیم مطلع بشیم تا مطمئن بشیم همه task ها کارشون رو به درستی به پایان رسوندن. مشکل اینجاست که زمان به اتمام رسیدن کار هر task متفاوته. در این مواقع از کلاسی به اسم DispatchGroup استفاده میکنیم. این کلاس سه متد اصلی داره:enter() 
leave()
notify()پیش از اجرای هر task متد enter()  و پس از پایان کارش متد leave() فراخوانی می‌شه:DispatchGroupبعد از اینکه به ازای هر بار فراخوانیِ متد enter، متدِ leave فراخوانی شد، ما از طریق متد notify از اتمام کار همه task ها مطلع می‌شیم.پایاناگر سوالی داشتین حتما بپرسید.امیدوارم از خوندن این مقاله لذت برده باشید? ? مراجع و منابعhttps://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2#toc-anchor-004https://medium.com/@deda9/swift-concurrency-programming-dispatch-queues-9d804cf277f3</description>
                <category>متین عبداللهی</category>
                <author>متین عبداللهی</author>
                <pubDate>Wed, 09 Oct 2019 15:23:50 +0330</pubDate>
            </item>
            </channel>
</rss>