وضع عدم الاتصال على iOS وميزات تنفيذه على عالم



أرسلت بواسطة Ekaterina Semashko ، قوي جونيور دائرة الرقابة الداخلية المطور ، DataArt

القليل عن المشروع: تطبيق جوال لمنصة iOS ، مكتوب في Swift. الغرض من التطبيق هو القدرة على مشاركة بطاقات الخصم بين موظفي الشركة وأصدقائهم.

كان أحد أهداف المشروع هو تعلم التقنيات والمكتبات الشعبية وممارستها. تم اختيار مجال لتخزين البيانات المحلية ، وتم استخدام Alamofire للعمل مع الخادم ، وتم استخدام تسجيل الدخول إلى Google للمصادقة ، وتم استخدام PINRemoteImage لتحميل الصور.

المهام الرئيسية للتطبيق:

  • إضافة خريطة وتعديلها وحذفها ؛
  • عرض بطاقات الآخرين ؛
  • البحث عن بطاقات حسب اسم المتجر / اسم المستخدم ؛
  • إضافة بطاقات إلى المفضلة لديك للوصول السريع.

تم افتراض القدرة على استخدام التطبيق دون الاتصال بالشبكة من البداية ، ولكن فقط في وضع القراءة. أي يمكننا عرض معلومات حول البطاقات ، لكن لا يمكننا تعديلها بدون الإنترنت. لهذا ، كان لدى التطبيق دائمًا نسخة من جميع البطاقات والعلامات التجارية لقاعدة البيانات من الخادم ، بالإضافة إلى قائمة المفضلة للمستخدم الحالي. تم تنفيذ البحث محليًا.

قررنا لاحقًا توسيع وضع عدم الاتصال عن طريق إضافة وضع تسجيل. تم تخزين المعلومات حول التغييرات التي أجراها المستخدم وتزامنها عند ظهور اتصال بالإنترنت. ستتم مناقشة تطبيق وضع القراءة والكتابة دون اتصال.



ما هو ضروري لوضع غير متصل بالشبكة بالكامل في تطبيق الهاتف المحمول؟ نحتاج إلى إزالة اعتماد المستخدم على جودة اتصال الإنترنت ، وعلى وجه الخصوص:

  1. أزل اعتماد الاستجابات للمستخدم على تصرفاته في واجهة المستخدم من الخادم. بادئ ذي بدء ، سوف يتفاعل الطلب مع التخزين المحلي ، ثم سيتم إرساله إلى الخادم.
  2. تحديد وتخزين التغييرات المحلية.
  3. قم بتطبيق آلية التزامن - عندما يظهر اتصال بالإنترنت ، فأنت بحاجة إلى إرسال التغييرات إلى الخادم.
  4. أظهر للمستخدم التغييرات التي تمت مزامنتها ، والتي ليست كذلك.

النهج غير متصل الأول


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

في الإصدار السابق ، كان هناك اتصال قوي بين طبقة تخزين البيانات وطبقة الشبكة. كانت آلية التعامل مع البيانات كما يلي: أولاً تم تقديم طلب إلى الخادم من خلال NetworkManager class ، انتظرنا النتيجة ، بعد أن تم حفظ البيانات في قاعدة البيانات من خلال فئة السجل. ثم أعطيت النتيجة لواجهة المستخدم ، كما هو موضح في الرسم التخطيطي.


لتنفيذ النهج الأول في وضع عدم الاتصال ، قمت بفصل طبقة تخزين البيانات وطبقة الشبكة ، حيث قدمت فئة تدفق جديدة تتحكم في الترتيب الذي تم من خلاله استدعاء NetworkManager و Repository. الآن يتم حفظ البيانات لأول مرة في قاعدة البيانات من خلال فئة السجل ، ثم يتم إرسال النتيجة إلى واجهة المستخدم ، ويستمر المستخدم في العمل مع التطبيق. في الخلفية ، يتم تقديم طلب إلى الخادم ، بعد الاستجابة ، يتم تحديث المعلومات الموجودة في قاعدة البيانات وواجهة المستخدم.


العمل مع معرفات الكائنات


مع البنية الجديدة ، ظهرت العديد من المهام الجديدة ، واحدة منها تعمل مع كائنات id. في السابق ، تلقيناهم من الخادم عند إنشاء الكائن. ولكن الآن تم إنشاء الكائن محليًا ، وفقًا لذلك ، كان من الضروري إنشاء معرّف وبعد تحديث المزامنة لهوية الحالية. لقد صادفت هنا الحد الأول للعالم: بعد إنشاء كائن ، لا يمكنك تغيير مفتاحه الأساسي.

كان الخيار الأول هو التخلي عن المفتاح الأساسي في الكائن ، وجعل معرف حقل منتظم. ولكن في الوقت نفسه ، فقدت مزايا استخدام المفتاح الأساسي: فهرسة Realm ، التي تسرع من إحضار الكائن ، والقدرة على تحديث الكائن بعلامة الإنشاء (إنشاء كائن في حالة عدم وجوده) ، والامتثال لتفرد الكائن.

أردت حفظ المفتاح الأساسي ، لكن لا يمكن أن يكون معرف الكائن من الخادم. نتيجة لذلك ، كان حل العمل هو الحصول على معرّفين ، أحدهما خادم ، وحقل اختياري ، والثاني محلي ، والذي سيكون هو المفتاح الأساسي.

نتيجة لذلك ، يتم إنشاء معرّف محلي على العميل عند إنشاء الكائن محليًا ، وفي الحالة التي يكون فيها الكائن من الخادم ، يكون مساويًا لمعرف الخادم. نظرًا لوجود قاعدة بيانات واحدة في تطبيق الحقيقة ، عند تلقي البيانات من الخادم ، يتم تحديث الكائن باستخدام المعرف المحلي الحالي ، ويعمل فقط معه. عند إرسال البيانات إلى الخادم ، يتم إرسال معرف الخادم.

تخزين التغييرات غير المتزامنة


يجب تخزين التغييرات على الكائنات التي لم يتم إرسالها إلى الخادم محليًا. يمكن تنفيذ ذلك بالطرق التالية:

  1. إضافة الحقول إلى الكائنات الموجودة
  2. تخزين كائنات غير متزامنة في جداول منفصلة ؛
  3. تخزين التغييرات حقل الفردية في بعض التنسيق.


أنا لا أستخدم كائنات Realm مباشرة في فصولي ، لكنني أقوم بتعيينها بنفسي لتجنب مشاكل التعدد. تتم التحديثات التلقائية للواجهة باستخدام نماذج نتائج التحديث التلقائي ، حيث أشترك في طلبات التحديث. الطريقة الأولى فقط هي التي عملت مع بنائي الحالي ، وبالتالي فقد وقع الاختيار في إضافة الحقول إلى الكائنات الموجودة.

مر كائن الخريطة بمعظم التغييرات:

  • متزامنة - هل هناك بيانات على الخادم ؛
  • تم حذف - صحيح ، إذا تم حذف البطاقة محليًا فقط ، فستكون المزامنة مطلوبة.

المعرفات التي تمت مناقشتها في الجزء السابق:

  • localId - المفتاح الأساسي للكيان في التطبيق ، إما مساويًا لمعرف الخادم أو الذي تم إنشاؤه محليًا ؛
  • serverId - معرف من الخادم.

بشكل منفصل الجدير بالذكر هو تخزين الصور. في جوهرها ، تمت إضافة diskURL الحقل المرفق إلى حقل serverURL من الصورة على الخادم ، والذي يخزن عنوان الصورة غير المتزامنة المحلية. عند مزامنة الصورة ، تم حذف الصورة المحلية حتى لا تسد ذاكرة الجهاز.

تزامن الخادم


للمزامنة مع الخادم ، تمت إضافة العمل باستخدام Reachability ، بحيث تظهر آلية المزامنة عند ظهور الإنترنت.

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

لإرسال التغييرات ، كان من الممكن تنفيذ تحديثات مجمعة أو إرسال التغييرات في صفيف أو تقديم طلب كبير لمزامنة جميع البيانات. ولكن بحلول ذلك الوقت ، كان المطور الخلفي مشغولًا بالفعل في مشروع آخر ، ولم يساعدنا إلا في وقت فراغنا ، لذلك نقوم بإنشاء طلب لكل نوع من أنواع التغيير.

لقد طبقت قائمة الانتظار من خلال OperationQueue ولف كل طلب في عملية غير متزامنة. تعتمد بعض العمليات على بعضها البعض ، على سبيل المثال ، لا يمكننا تحميل صورة الخريطة قبل إنشاء الخريطة ، لذلك أضفت تبعية عملية الصورة إلى عملية الخريطة. أيضًا ، أعطيت عملية تحميل الصور إلى الخادم أولوية أقل من أي شخص آخر ، وأضفتها إلى قائمة الانتظار أيضًا بسبب ثقلها.

عند التخطيط لوضع عدم الاتصال ، كان السؤال الكبير هو حل التعارضات مع الخادم أثناء المزامنة. لكن عندما وصلنا إلى هذه النقطة أثناء التنفيذ ، أدركنا أن الحالة التي يغير فيها المستخدم نفس البيانات على أجهزة مختلفة أمر نادر للغاية. لذلك يكفي أن ننفذ آخر كاتب يفوز بآلية. أثناء المزامنة ، تعطى الأولوية دائمًا للتغييرات غير المرسلة على العميل ، ولا يتم فركها.

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

حل إضافي عند العمل مع Realm


عند العمل مع Realm واجهت العديد من المشاكل. ربما ستكون هذه التجربة مفيدة أيضًا لشخص ما.

عند الفرز حسب السلسلة ، يذهب الترتيب وفقًا لترتيب الأحرف في UTF-8 ، لا يوجد دعم بحث حساس لحالة الأحرف. نحن نواجه موقفًا حيث تأتي الأسماء في الأحرف الصغيرة بعد الأسماء الموجودة في الحالة العليا ، على سبيل المثال: Magnet و Pyaterochka و Ribbon. إذا كانت القائمة كبيرة جدًا ، فستكون جميع الأسماء الموجودة في الأحرف الصغيرة في الأسفل ، وهو أمر غير سار للغاية.

للحفاظ على ترتيب الفرز ، بصرف النظر عن الحالة ، اضطررنا إلى تقديم حقل اسم خفيض جديد وتحديثه عند تحديث الاسم والفرز به.

بالإضافة إلى ذلك ، تمت إضافة حقل جديد للفرز من خلال وجود بطاقة في المفضلة ، لأن هذا يتطلب في جوهره استعلام فرعي لعلاقات الكائن.

عند البحث في مجال ، هناك طريقة CONTAINS [c]٪ @ لعمليات البحث غير الحساسة لحالة الأحرف. ولكن ، للأسف ، لا يعمل إلا مع الأبجدية اللاتينية. بالنسبة للعلامات التجارية الروسية ، كان علينا أيضًا إنشاء حقول منفصلة والبحث عنها. لكن فيما بعد اتضح أنه في أيدينا لاستبعاد الشخصيات الخاصة عند البحث.



كما ترون ، من الممكن لتطبيقات الهاتف المحمول تطبيق وضع غير متصل بالشبكة مع حفظ التغييرات والمزامنة مع القليل من الدم ، وأحيانًا حتى مع الحد الأدنى من التغييرات على الواجهة الخلفية.

على الرغم من بعض الصعوبات ، يمكنك استخدام Realm لتنفيذه ، مع تلقي جميع المزايا في شكل تحديثات حية ، وتصميم نسخة خالية وواجهة برمجة تطبيقات ملائمة.

لذلك لا يوجد سبب يمنع المستخدمين من الوصول إلى البيانات في أي وقت ، بغض النظر عن جودة الاتصال.

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


All Articles