API لجلب غير متزامن عن بعد باستخدام Apple Combine



Combine عبارة عن إطار عمل Swift تفاعلي تم تنفيذه مؤخرًا على جميع منصات Apple ، بما في ذلك Xcode 11 . باستخدام Combine ، من السهل جدًا معالجة تسلسل القيم التي تظهر بشكل غير متزامن مع مرور الوقت. كما أنه يبسط التعليمات البرمجية غير المتزامنة عن طريق التخلي عن عمليات الاستعادة المتداخلة المعقدة المعقدة.

لكن الدراسة " دمج" في البداية قد لا تبدو بهذه البساطة. الحقيقة هي أن "اللاعبين" الأساسيين في Combine هم مفاهيم مجردة مثل "الناشرون" الناشرون ، "المشتركون" المشتركون والمشغلون ، والتي بدونها لن يكون من الممكن التقدم في فهم منطق الدمج بين الأداء. ومع ذلك ، نظرًا لحقيقة أن Apple توفر للمطورين "ناشرين" جاهزين و "مشتركين" ومشغلين ، فإن الشفرة المكتوبة مع Combine مضغوطة جدًا ويمكن قراءتها جيدًا.

سترى هذا في مثال تطبيق مرتبط باسترداد معلومات الأفلام بشكل غير متزامن من قاعدة بيانات TMDb الشائعة جدًا الآن . سننشئ تطبيقين مختلفين: UIKit و SwiftUI ، ونبين كيف يعمل Combine معهم.



آمل أن يجعل هذا المقال أسهل بالنسبة لك لتعلم الجمع . يمكن العثور على رمز جميع التطبيقات التي تم تطويرها في هذه المقالة على جيثب .

الجمع بين عدة عناصر رئيسية:

الناشر الناشر .




الناشرون الناشرون هم أنواع تقدم القيم لكل من يهتم. يتم تطبيق مفهوم "الناشر" للناشر على " دمج " كبروتوكول ، وليس كنوع محدد. ارتبط بروتوكول Publisher أنواع عام لقيمة الإخراج وخطأ الفشل .
يستخدم "الناشر" الذي لا ينشر أبداً أي خطأ في TYPE Never لخطأ الفشل .





توفر Apple للمطورين تطبيقات محددة لـ "الناشرين" الجاهزين: فقط ، المستقبل ، فارغ ، مؤجل ، تسلسل ، @ منشور ، إلخ. تتم إضافة "الناشرين" أيضًا إلى فصول التأسيس : URLSession ، < NotificationCenter ، Timer .

"المشترك" المشترك .




إنه أيضًا بروتوكول بروتوكول يوفر واجهة "الاشتراك" لقيم من "ناشر". لقد ربط أنواع عام لأخطاء الإدخال والفشل . من الواضح ، يجب أن تتطابق أنواع Publisher Publisher ومشترك المشترك .



لأي ناشر Publisher ، يوجد مشتركان مدمجان : sink and assign :



يعتمد مصدر "المشترك" على غلقين: يتم تنفيذ إغلاق واحد ، receValue ، عندما تحصل على القيم ، ويتم تنفيذ الإغلاق الثاني ، receCompletion ، عند اكتمال "المنشور" (عادة أو مع وجود خطأ).



تعيين "المشترك" ، تقدم كل قيمة مستلمة ، نحو key Path المحدد.

اشتراك " اشتراك ".




أولاً ، يقوم الناشر " الناشر " بإنشاء اشتراك " المشترك " وتوصيله إلى المشترك " المشترك " من خلال طريقة الاستلام (الاشتراك :) :



بعد ذلك ، يمكن للاشتراك إرسال قيمه إلى "المشتركين" من المشتركين باستخدام طريقتين:



إذا كنت قد انتهيت من العمل مع اشتراك الاشتراك ، فيمكنك استدعاء طريقة الإلغاء () :



"الموضوع" الموضوع .




هذا بروتوكول بروتوكول يوفر واجهة لكل من العملاء ، لكل من "الناشر" و "المشترك". في الأساس ، الموضوع " الموضوع " هو الناشر " الناشر " ، والذي يمكنه قبول إدخال القيمة المدخلة والتي يمكنك استخدامها "لضخ" القيم في الدفق عن طريق استدعاء أسلوب send () . قد يكون ذلك مفيدًا عند تكييف التعليمات البرمجية الضرورية الحالية في " دمج النماذج".

مشغل المشغل .




باستخدام المشغل ، يمكنك إنشاء "ناشر" ناشر جديد من ناشر آخر "ناشر" عن طريق تحويل القيم وتصفيةها وحتى دمجها من قبل العديد من الناشرين السابقين.



ترى هنا الكثير من أسماء المشغلين المألوفة: صورة مضغوطة وخريطة ومرشح وإسقاط أولاً وإلحاق .

الناشرين مؤسسة الناشرين في صلب المؤسسة .


توفر Apple أيضًا للمطورين العديد من وظائف Combine المضمنة بالفعل في Foundation <framework ، أي ناشرو الناشرين ، للقيام بمهام مثل جلب البيانات باستخدام URLSession ، والعمل مع الإشعارات باستخدام خصائص Notification و Timer ومراقبة تستند إلى KVO . سيساعدنا هذا التوافق المدمج في دمج إطار Combine في مشروعنا الحالي.
لمعرفة المزيد حول هذا الموضوع ، راجع مقالة "البرنامج التعليمي الشامل للاندماج في Swift" .

ماذا نتعلم كيف نفعل مع الجمع ؟


في هذه المقالة ، سوف نتعلم كيفية استخدام إطار Combine لجلب بيانات الأفلام من موقع TMDb . إليك ما سوف ندرسه معًا:

  • استخدام "الناشر" Future لإنشاء إغلاق مع Promise لقيمة واحدة: إما قيمة أو خطأ.
  • استخدام "الناشر" URLSession.datataskPublisher "للاشتراك" في بيانات البيانات المنشورة بواسطة UR L.
  • استخدام عامل التشغيل tryMap لتحويل بيانات البيانات باستخدام ناشر Publisher آخر.
  • استخدام مشغل فك التشفير لتحويل بيانات البيانات إلى كائن فك تشفير ونشرها لنقلها إلى عناصر لاحقة من السلسلة.
  • استخدام عامل التشغيل "للاشتراك" في ناشر "ناشر" باستخدام عمليات الإغلاق.
  • استخدم جملة التعيين "للاشتراك" في "الناشر" للناشر وتعيين القيمة التي key Path المحدد.

المشروع الأولي


قبل أن نبدأ ، يجب علينا التسجيل لاستلام مفتاح API على موقع TMDb . تحتاج أيضًا إلى تنزيل المشروع الأولي من مستودع جيثب .
تأكد من وضع مفتاح API الخاص بك في فئة MovieStore الصفية لتدع apiKey ثابتًا.



فيما يلي العناصر الأساسية التي سننشئ منها مشروعنا:

  • 1. يوجد داخل الملف Movie.swift نماذج سوف نستخدمها في مشروعنا. ينفّذ هيكل الجذر لـ MoviesResponse بروتوكول فك التشفير ، وسوف نستخدمه عند فك تشفير بيانات JSON في نموذج. تحتوي بنية MoviesResponse على خاصية نتائج ، والتي تنفذ أيضًا بروتوكول فك الترميز وهي عبارة عن مجموعة من الأفلام [فيلم] . هي التي تهمنا:



  • 2. التعداد MovieStoreAPIError تنفذ بروتوكول خطأ . ستستخدم API بنا هذا التعداد لتمثيل أنواع مختلفة من الأخطاء: أخطاء استرجاع URL urlError ، وأخطاء فك تشفير أخطاء فك التشفير ، واستجابة أخطاء جلب بيانات الأخطاء.



  • 3. لدينا API لديها بروتوكول MovieService مع طريقة واحدة ، fetchMovies (من نقطة النهاية: Endpoint) ، والذي يختار [Movie] الأفلام على أساس معلمة نقطة النهاية . Endpoint نفسها عبارة عن تعداد يمثل نقطة نهاية للوصول إلى قاعدة بيانات TMDb لجلب أفلام مثل nowPlaying (الأحدث) والشعبية (الشائعة) و topRated (أعلى) والقادمة (قريبًا على الشاشة).



  • 4. فئة MovieStore هي فئة معينة تنفذ بروتوكول MovieService لجلب البيانات من موقع TMDb . داخل هذه الفئة ، نطبق طريقة fetchMovies (...) باستخدام الجمع .



  • 5. فئة MovieListViewController هي فئة ViewController الرئيسية التي نستخدم فيها طريقة sink "للاشتراك" في طريقة جلب فيلم fetchMovies (...) ، والتي تُرجع المستقبل "ناشر" ، ثم نقوم بتحديث جدول TableView مع بيانات فيلم جديدة الأفلام باستخدام API DiffableDataSourceSnapshot الجديد.

قبل البدء ، دعونا نلقي نظرة على بعض المكونات المدمجة التي سنستخدمها في API لاسترجاع البيانات عن بُعد.

"اشترك" في "الناشر" باستخدام المصارف وإغلاقاتها.


إن أسهل طريقة "للاشتراك" في "الناشر" للناشر هي استخدام sink مع الإغلاقات الخاصة به ، وسيتم تنفيذ أحدهما كلما حصلنا على قيمة جديدة ، والآخر عندما ينتهي "الناشر" من تسليم القيم .



تذكر أنه في Combine ، يُرجع كل "اشتراك" إلغاء ، وسيتم حذفه بمجرد مغادرة سياقنا. من أجل الحفاظ على "اشتراك" لفترة أطول ، على سبيل المثال ، للحصول على قيم بشكل غير متزامن ، نحتاج إلى حفظ "الاشتراك" في خاصية subscription1 . هذا سمح لنا باستمرار الحصول على جميع القيم (7،8،3،4) .

المستقبل بشكل غير متزامن "ينشر" قيمة واحدة: إما قيمة أو خطأ فشل .


في إطار Combine ، يمكن استخدام "الناشر" Future للحصول بشكل غير متزامن على TYPE من النتيجة باستخدام الإغلاق. إغلاق يحتوي على معلمة واحدة - وعد ، والتي هي وظيفة من TYPE (النتيجة <الإخراج ، الفشل>) -> الفراغ .

دعونا نلقي نظرة على مثال بسيط لفهم كيفية عمل ناشر Future :



نخلق المستقبل بنتيجة ناجحة من TYPE Int وخطأ في TYPE Never . داخل إغلاق Future ، نستخدم DispatchQueue.main.asyncAfter (...) لتأخير تنفيذ التعليمات البرمجية بمقدار ثانيتين ، وبالتالي محاكاة السلوك غير المتزامن. داخل الإغلاق ، نعود إلى الوعد مع نتيجة ناجحة للوعد (.success (...)) كقيمة عدد صحيح عشوائي في النطاق بين 0 و 100 . بعد ذلك ، نستخدم اشتراكين مستقبليين - قابلان للإلغاء وإلغاء - ونحصل كلاهما على نفس النتيجة ، على الرغم من إنشاء رقم عشوائي من الداخل.
1. تجدر الإشارة إلى أن "ناشر" Future in Combine لديه بعض الميزات السلوكية مقارنة بـ "الناشرين" الآخرين:

  • ينشر "الناشر" المستقبل دائمًا "قيمة واحدة ( قيمة أو خطأ) ، وهذا يكمل عمله.

  • المستقبل "ناشر" هو فئة ( reference type ) فئة ، على عكس "الناشرين" الآخرين ، والتي هي أساسًا هياكل ( value type ) ، ويتم تمريرها كمعلمة إغلاق التعهد ، الذي يتم إنشاؤه فور تهيئة مثيل "الناشر" في المستقبل . أي ، يتم إرسال إغلاق الوعد قبل أن يشترك أي مشترك " مشترك " في مثيل "ناشر" المستقبل على الإطلاق. لا يتطلب "ناشر" Future "أي مشترك" على الإطلاق لعمله ، كما يتطلب جميع الناشرين العاديين الآخرين. هذا هو السبب في أن النص "مرحبًا من داخل المستقبل!" يتم طباعته مرة واحدة فقط في الرمز أعلاه.

  • "الناشر" المستقبلي هو "ناشر" eager (غير صبور) ، على عكس معظم "الناشرين" البطيئين الآخرين ("النشر" فقط إذا كان هناك "اشتراك"). بمجرد إغلاق ناشر Future للوعد ، يتم تذكر النتيجة ثم تسليمها إلى "المشتركين" الحاليين والمستقبليين. من التعليمة البرمجية أعلاه ، نرى أنه عندما "تشترك" بشكل متكرر في بالوعة للناشر المستقبلي ، فإنك تحصل دائمًا على نفس القيمة "العشوائية" (في هذه الحالة 6 ، ولكن يمكن أن تكون مختلفة ، ولكنها دائمًا متشابهة) ، على الرغم من استخدامها في الإغلاق قيمة كثافة العمليات العشوائية.

يسمح لك منطق "الناشر" Future هذا باستخدامه بنجاح في حفظ نتيجة محتسبة غير متزامنة تستهلك الموارد ولا تزعج "الخادم" بسبب "اشتراكات" متعددة لاحقة.

إذا كان هذا المنطق لـ "ناشر" Future لا يناسبك وترغب في تسمية مستقبلك كسولًا وفي كل مرة تحصل فيها على قيم Int عشوائية جديدة ، فيجب عليك "التفاف" Future in Deferred :



سنستخدم Future بطريقة كلاسيكية ، كما هو مقترح في Combine ، أي "ناشر" محسوب غير متزامن محسوب "مشترك".

ملاحظة 2. نحتاج إلى تقديم ملاحظة أخرى فيما يتعلق بـ "الاشتراك" باستخدام مصدر إلى "الناشر" غير المتزامن. تقوم طريقة الحوض بإرجاع AnyCancellable ، والتي لا نتذكرها باستمرار. هذا يعني أن Swift ستدمر AnyCancellable في الوقت الذي تغادر فيه السياق المحدد ، وهو ما يحدث في السلسلة main thread . وبالتالي ، اتضح أن AnyCancellable يتم إتلافه قبل أن يبدأ الإغلاق مع Promise في main thread . عندما يتم تدمير AnyCancellable ، يتم استدعاء طريقة الإلغاء الخاصة بها ، والتي في هذه الحالة تلغي "الاشتراك". هذا هو السبب في أننا نتذكر "اشتراكاتنا" الخاصة بالمستقبل في المستقبل في المتغيرات cancellablecancellable1 أو في Set <AnyCancellable> () .


استخدام Combine لجلب الأفلام من موقع TMDb .


بادئ ذي بدء ، افتح مشروع بدء التشغيل MovieStore.swift ملف MovieStore.swift وأسلوب fetchMovies بتطبيق فارغ:



باستخدام طريقة fetchMovies ، يمكننا اختيار أفلام متنوعة عن طريق تعيين قيم محددة لمعلمة إدخال نقطة النهاية الخاصة بـ TYPE Endpoint . TYPE Endpoint هو تعداد يأخذ القيم الآنالتشغيل (الحالي) ، والقادم ( يطرح إلى الشاشة قريبًا) ، والشعبية (الشائعة) ، topRated (أعلى):



لنبدأ بتهيئة Future بإغلاق callback . تلقى المستقبل فإننا سنرد بعد ذلك.



داخل إغلاق callback ، نقوم بإنشاء URL للقيمة المطابقة لمعلمة إدخال نقطة النهاية باستخدام دالة generURL (مع نقطة النهاية: نقطة النهاية) :



إذا تعذر إنشاء URL الصحيح ، فسنرجع خطأً باستخدام الوعد (.failure (.urlError (...)) ، وإلا فإننا نمضي قدمًا وننفذ URLSession.dataTaskPublisher "الناشر".

"للاشتراك" في بيانات من URL معين URL يمكننا استخدام طريقة datataskPublisher المضمنة في فئة URLSession ، والتي تأخذ URL كمعلمة وتُرجع الناشر "الناشر" مع بيانات مخرجات المجموعة (البيانات: البيانات ، الاستجابة: URLResponse) وخطأ URLError .



لتحويل ناشر Publisher إلى ناشر Publisher آخر ، استخدم عامل التشغيل tryMap . مقارنةً بالخريطة ، يمكن لمشغل tryMap أن يلقي خطأ " خطأ" داخل الرميات ، مما يعيدنا لناشر الناشر الجديد.

في الخطوة التالية ، سوف نستخدم مشغل tryMap للتحقق من كود http statusCode لاستجابة الاستجابة للتأكد من أن قيمتها تتراوح بين 200 و 300 . إذا لم يكن كذلك ، فإننا نلقي رمية قيمة خطأ تعداد MovieStoreAPIError تعداد. بخلاف ذلك (عند عدم وجود أخطاء) ، فإننا ببساطة نعيد بيانات البيانات المستلمة إلى الناشر التالي في السلسلة "الناشر".



في الخطوة التالية ، سوف نستخدم مشغل فك التشفير ، الذي يقوم بترميز بيانات إخراج JSON الخاصة بـ "الناشر" tryMap السابق في MovieResponse <Model باستخدام JSONDecoder .



... تم تكوين jsonDecoder لتنسيق تاريخ محدد:



للمعالجة المراد تنفيذها في main thread الرسائل main thread ، سنستخدم عامل التلقي (على :) ونمرر RunLoop.main كمعلمة إدخال. سيسمح هذا "للمشترك" بالحصول على قيمة القيمة في الخيط main .



أخيرًا ، وصلنا إلى نهاية سلسلة التحويل الخاصة بنا ، وهناك نستخدم sink للحصول على اشتراك " اشتراك " في "سلسلة" تشكيل الناشرين "الناشرين". لتهيئة مثيل لفئة Sink ، نحتاج إلى شيئين ، رغم أن أحدهما اختياري:

  • تلقى الإغلاقالقيمة:. سيتم استدعاؤها عندما يحصل اشتراك "اشتراك" على قيمة جديدة من الناشر " الناشر ".

  • receCompletion إغلاق : (اختياري). سيتم استدعاؤها بعد أن ينهي الناشر "الناشر" نشر القيمة ، وسيُعطى تعداد الإكمال ، والذي يمكننا استخدامه للتحقق مما إذا كان "نشر" القيم قد اكتمل حقًا أم أن الإكمال كان بسبب خطأ.

داخل إغلاق receValue ، ندعو ببساطة إلى الوعد بخيار .success وقيمة $ 0.results ، وهو في حالتنا عبارة عن مجموعة من أفلام الأفلام . داخل إغلاق receCompletion ، نتحقق مما إذا كان للإكمال خطأ في الخطأ ، ثم نمرر خطأ الوعد المقابل مع خيار .failure .



لاحظ أننا نجمع هنا جميع الأخطاء "التي تم طرحها" في المراحل السابقة من "سلسلة الناشر".

بعد ذلك ، نحفظ اشتراك "الاشتراك" في الخاصية Set <AnyCancellable> () .
والحقيقة هي أن اشتراك "الاشتراك" قابل للإلغاء ، فهو بروتوكول يدمر ويمسح كل شيء بعد الانتهاء من وظيفة fetchMovies . لضمان الحفاظ على اشتراك "الاشتراك" حتى بعد الانتهاء من هذه الوظيفة ، نحتاج إلى تذكر اشتراك " الاشتراك " في المتغير الخارجي لوظيفة fetchMovies . في حالتنا ، نستخدم خاصية الاشتراكات ، التي لديها نوع Set <AnyCancellable> () ونستخدم طريقة .store (في: & self.subscriptions) ، والتي تضمن سهولة استخدام "الاشتراك" بعد انتهاء وظيفة fetchMovies من عملها.



هذا يخلص إلى تكوين طريقة fetchMovies لاختيار الأفلام من قاعدة بيانات TMDb باستخدام Combine framework. تأخذ طريقة fetchMovies ، كمعلمة الإدخال من ، قيمة التعداد Endpoint ، أي الأفلام المحددة التي تهمنا:

.nowPlaying - الأفلام التي تظهر حاليًا على الشاشة ،
.upcoming - أفلام ستأتي قريبًا ،
.أفلام شعبية -
.topRated - أعلى الأفلام ، وهذا هو ، مع تصنيف عالية جدا.
دعونا نحاول تطبيق API هذه على تصميم التطبيق مع واجهات مستخدم UIKit المعتادة في شكل Table View Controller :



وإلى تطبيق تم تصميم واجهة المستخدم الخاصة به باستخدام إطار عمل SwiftUI التعريفي الجديد:



"اشترك" في أفلام من أفلام View Controller المعتادة.


ننتقل إلى ملف MovieListViewController.swift وفي طريقة viewDidLoad استدعاء الأسلوب fetchMovies .



داخل طريقة fetchMovies الخاصة بنا ، نستخدم movieAPI التي تم تطويرها مسبقًا وطريقة fetchMovies الخاصة بها مع المعلمة .nowPlaying كنقطة النهاية لمعلمة from from . أي أننا سنختار الأفلام الموجودة حاليًا على شاشات دور السينما.



تقوم طريقة movieAPI.fetchMovies (من: .nowPlaying) بإرجاع "ناشر" Future ، نشترك فيه باستخدام sink ، ونزوده بإغلاقين . في إغلاق receCompletion ، نتحقق مما إذا كان هناك خطأ في الخطأ ونعرض تحذيرًا طارئًا لتنبيه المستخدم مع ظهور رسالة الخطأ.



في عملية الإغلاق ReceValue ، ندعو طريقة createSnapshot ونمرر الأفلام المختارة إليها .



تقوم دالة generSnapshot بإنشاء NSDiffableDataSourceSnapshot جديدة باستخدام أفلامنا ، وتطبق اللقطة الناتجة على diffableDataSource من طاولتنا.

نطلق التطبيق ونشاهد كيف تعمل UIKit مع "الناشرين" و "المشتركين" من إطار الجمع . هذا تطبيق بسيط للغاية لا يسمح لك بمشاهدة مجموعات مختلفة من الأفلام - تظهر الآن على الشاشة ، شعبية ، ذات تصنيف عالي أو تلك التي ستظهر على الشاشة في المستقبل القريب. نرى فقط تلك الأفلام التي ستظهر على الشاشة ( .القادمة ). بالطبع ، يمكنك القيام بذلك عن طريق إضافة أي عنصر UI لتعيين قيم تعداد نقطة النهاية ، على سبيل المثال ، باستخدام Stepper أو Segmented Control ، ثم تحديث واجهة المستخدم. هذا معروف جيدًا ، لكننا لن نقوم بذلك في تطبيق يستند إلى UIKit ، ولكن نترك هذا لإطار عمل SwiftUI التعريفي الجديد.
يمكن العثور على رمز تطبيق يستند إلى UIKit على Github في CombineFetchAPICompleted-UIKit .

استخدم SwiftUI لعرض أفلام الأفلام

.
CombineFetchAPI-MY SwiftUI File -> New -> Project Single View App iOS :



UISwiftUI :



Movie.swift Model , TMDb MovieStore.swift , MovieStoreAPIError.swift MovieService.swift , MovieService Protocol :



SwiftUI , Codable , JSON , Identifiable , [Movie] List . SwiftUI Equatable Hashable , UIKit API UITableViewDiffableDataSource UIKit . < struct Movie , Equatable Hashable :


........................... .


Identifiable , Swift Identifiable , Hashable Equatable .

, SwiftUI , , endpoint , :



, UIKit , movieAPI.fetchMovies (from endpoint: Endpoint) , endpoint «» Future<[Movie, MovieStoreAPIError]> . Endpoint , , case Endpoint index :



, movies , indexEndpoint Endpoint . View Model , MoviesViewModel , ObservableObject . MoviesViewModel.swift View Model :



@Published : @Published var indexEndpoint: Int — , @Published var movies: [Movie] - . @Published indexEndpoint , indexEndpoint , $indexEndpoint .
MoviesViewModel «» $indexEndpoint «» AnyPublisher<[Movie], Never> , movieAPI.fetchMovies (from: Endpoint (index: indexPoint)) flatMap .



«» «» «» assing (to: \.movies, on: self) «» movies . «» assing (to: \.movies, on: self) , «» , Never . ? replaceError(with: [ ]) , movies .

SwiftUI .

, View Model , UI . ContentView.swift View Model @EnvironmentObject var moviesViewModel Text(«Hello, World!»)
Text("\(moviesViewModel.indexEndpoint)") , indexEndpoint .



View Model indexEndpoint = 2 , , ( Upcoming ):



UI , . Stepper :



Picker :



«» $moviesViewModel.indexEndpoint View Model , ( ) :



List ForEach movie :



moviesViewModel.movies View Model :



«» $moviesViewModel.movies $ , . moviesViewModel.movies .

, , URL Movie :



Thomas Ricouard MovieSwiftUI .

movies , UIImage ImageService , Combine fetchImage , «» AnyPublisher<UIImage?, Never> :



final class ImageLoader: ObservableObject , ObservableObject @Published image: UIImage? :



, ObservableObject - objectWillChange . SwiftUI , - , , Views , . objectWillChange , @Published . - objectWillChange . .
ImageLoader @Published var image:UIImage? . , ImageLoader , «» $image «» loadImage() , poster size @Published var image:UIImage? . objectWillChange .
, , imageLoader ImageLoader :



View MoviePosterImage :



ContentView :





SwiftUI Github CombineFetchAPI-NOError .

.


, TMDb . movieAPI.fetchMovies (from endpoint: Endpoint) , , «» Future<[Movie, MovieStoreAPIError]> .

, , View Model @Published moviesError: MovieStoreAPIError? , . Optional , nil , :



, moviesError , MoviesViewModel «» sink :



moviesError UI , nil



AlertView :



, API :



SwiftUI Github CombineFetchAPI-Error .

, Future<[Movie],MovieStoreAPIError> , AnyPublisher<[Movie], Never> fetchMoviesLight :



( Never ) «» assign(to: \.movies, on: self) :



:



استنتاج


Combine values . , Combine , — . Combine upstream «» Publishers , «» Subscribers . Combine , Foundation , Foundation .



SwiftUI Combine < @ObservableObject , @Binding @EnvironmentObject .
iOS Apple .

:

Fetching Remote Async API with Apple Combine Framework
try! Swift NYC 2019 — Getting Started with Combine
«The ultimate Combine framework tutorial in Swift».

Combine: Asynchronous Programming with Swift

Introducing Combine — WWDC 2019 — Videos — Apple Developer. session 722
( 722 « Combine» )

Combine in Practice — WWDC 2019 — Videos — Apple Developer. session 721
( 721 « Combine» )

SwiftUI & Combine: . SwiftUI Combine .

MovieSwiftUI .

Visualize Combine Magic with SwiftUI Part 1 ()
Visualize Combine Magic with SwiftUI – Part 2 (Operators, subscribing, and canceling in Combine)
Visualize Combine Magic with SwiftUI Part 3 (See Combine Merge and Append in Action)
Visualize Combine Magic with SwiftUI — Part 4

Visualize Combine Magic with SwiftUI — Part 5

Getting Started With the Combine Framework in Swift
Transforming Operators in Swift Combine Framework: Map vs FlatMap vs SwitchToLatest
Combine's Future
Using Combine
URLSession and the Combine framework

Source: https://habr.com/ru/post/ar476678/


All Articles