إنشاء تطبيق Android باستخدام Firebase وJetpack Compose

1- مقدمة

تاريخ آخر تعديل: 16‏/11‏/2022

إنشاء تطبيق Android باستخدام Firebase وJetpack Compose

في هذا الدرس التطبيقي حول الترميز، ستُنشئ تطبيق Android باسم Make It So. تم إنشاء واجهة المستخدم لهذا التطبيق بالكامل باستخدام Jetpack Compose، وهي حزمة أدوات حديثة من Android لإنشاء واجهة مستخدم أصلية. وهي سهلة الاستخدام وتتطلّب رمزًا أقل من كتابة ملفات ‎.xml وربطها بالأنشطة أو الأجزاء أو المشاهد.

إنّ الخطوة الأولى لفهم مدى كفاءة عمل Firebase وJetpack Compose معًا هي فهم بنية Android الحديثة. تجعل البنية الجيدة النظام سهل الفهم والتطوير والصيانة، لأنّها توضّح بوضوح كيفية تنظيم المكوّنات والتواصل مع بعضها. في عالم Android، تُعرف البنية المقترَحة باسم النموذج - العرض - نموذج العرض. يمثّل النموذج الطبقة التي تصل إلى البيانات في التطبيق. العرض هو طبقة واجهة المستخدم، ويجب ألا يعرف أيّ شيء عن منطق النشاط التجاري. وViewModel هو المكان الذي يتم فيه تطبيق منطق النشاط التجاري، ما يتطلّب أحيانًا من ViewModel استدعاء طبقة Model.

ننصحك بشدة بقراءة هذه المقالة لفهم كيفية تطبيق النموذج - العرض - نموذج العرض على تطبيق Android تم إنشاؤه باستخدام Jetpack Compose، لأنّ ذلك سيسهّل فهم قاعدة البيانات وإكمال الخطوات التالية.

التطبيق الذي ستصممه

Make It So هو تطبيق بسيط لقائمة المهام يتيح للمستخدم إضافة المهام وتعديلها وإضافة الإشارات والفِرق وأولويات تواريخ التسليم ووضع علامة على المهام كمكتملة. تعرض الصور أدناه الصفحتَين الرئيسيتَين لهذا التطبيق: صفحة إنشاء المهام والصفحة الرئيسية التي تتضمّن قائمة بالمهام التي تم إنشاؤها.

شاشة إضافة مهمة شاشة Make it So الرئيسية

ستضيف بعض الميزات غير المتوفّرة في هذا التطبيق:

  • مصادقة المستخدمين باستخدام البريد الإلكتروني وكلمة المرور
  • إضافة مستمع إلى مجموعة Firestore وجعل واجهة المستخدم تستجيب للتغييرات
  • إضافة عمليات تتبُّع مخصّصة لمراقبة أداء رمز معيّن في التطبيق
  • إنشاء زرّ لتفعيل الميزة أو إيقافها باستخدام ميزة "الإعداد عن بُعد" واستخدام الطرح على مراحل لإطلاقها

ما ستتعرّف عليه

  • كيفية استخدام "مصادقة Firebase" و"مراقبة الأداء" و"الإعداد عن بُعد" وCloud Firestore في تطبيق Android حديث
  • كيفية جعل واجهات برمجة تطبيقات Firebase تناسب بنية MVVM
  • كيفية عرض التغييرات التي تم إجراؤها باستخدام واجهات برمجة تطبيقات Firebase في واجهة مستخدم Compose

المتطلبات

  • استوديو Android الإصدار 4.0 والإصدارات الأحدث
  • محاكي Android مع المستوى 21 لواجهة برمجة التطبيقات أو إصدار أحدث
  • الإلمام بلغة البرمجة Kotlin

2- الحصول على نموذج التطبيق وإعداد Firebase

الحصول على رمز نموذج التطبيق

استنسِخ مستودع GitHub من سطر الأوامر:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

إنشاء مشروع على Firebase

أول ما عليك فعله هو الانتقال إلى وحدة تحكّم Firebase وإنشاء مشروع على Firebase من خلال النقر على الزر "+ إضافة مشروع"، كما هو موضّح أدناه:

وحدة تحكُّم Firebase

اتّبِع الخطوات التي تظهر على الشاشة لإكمال عملية إنشاء المشروع.

إضافة تطبيق Android إلى مشروعك على Firebase

في مشروعك على Firebase، يمكنك تسجيل تطبيقات مختلفة: لنظامَي التشغيل Android وiOS والويب وFlutter وUnity.

اختَر خيار Android، كما هو موضّح هنا:

نظرة عامة على مشروع Firebase

بعد ذلك، اتّبِع الخطوات التالية:

  1. أدخِل com.example.makeitso كاسم الحزمة، ويمكنك اختياريًا إدخال لقب. في هذا الدليل التعليمي حول الرموز البرمجية، لا تحتاج إلى إضافة شهادة التوقيع لتصحيح الأخطاء.
  2. انقر على التالي لتسجيل تطبيقك والوصول إلى ملف إعدادات Firebase.
  3. انقر على تنزيل google-services.json لتنزيل ملف الإعدادات وحفظه في الدليل make-it-so-android/app.
  4. انقر على التالي. بما أنّ حِزم تطوير البرامج (SDK) لمنصّة Firebase مضمّنة في ملف build.gradle في نموذج المشروع، انقر على التالي للانتقال إلى الخطوات التالية.
  5. انقر على متابعة إلى وحدة التحكّم لإنهاء العملية.

لكي يعمل تطبيق Make it So بشكل صحيح، عليك تنفيذ أمرَين في وحدة التحكّم قبل الانتقال إلى الرمز البرمجي: تفعيل مقدّمي المصادقة وإنشاء قاعدة بيانات Firestore.

إعداد المصادقة

أولاً، لنفعِّل ميزة "المصادقة" ليتمكّن المستخدمون من تسجيل الدخول إلى التطبيق:

  1. من قائمة الإنشاء، اختَر المصادقة، ثم انقر على البدء.
  2. من بطاقة طريقة تسجيل الدخول، اختَر البريد الإلكتروني/كلمة المرور وفعِّلها.
  3. بعد ذلك، انقر على إضافة مقدّم خدمة جديد واختَر غير محدّد وفعِّله.

إعداد Cloud Firestore

بعد ذلك، عليك إعداد Firestore. ستستخدم Firestore لتخزين مهام المستخدم الذي سجّل الدخول. سيحصل كل مستخدم على مستند خاص به ضمن مجموعة من قاعدة البيانات.

  1. في اللوحة اليمنى من "وحدة تحكّم Firebase"، وسِّع الإنشاء، ثم اختَر قاعدة بيانات Firestore.
  2. انقر على إنشاء قاعدة بيانات.
  3. اترك رقم تعريف قاعدة البيانات مضبوطًا على (default).
  4. اختَر موقعًا لقاعدة بياناتك، ثم انقر على التالي.
    بالنسبة إلى التطبيق الحقيقي، عليك اختيار موقع قريب من المستخدمين.
  5. انقر على البدء في وضع الاختبار. اقرأ بيان إخلاء المسؤولية عن قواعد الأمان.
    في الخطوات التالية من هذا القسم، ستضيف قواعد أمان لتأمين بياناتك. لا توزِّع تطبيقًا علنًا أو تعرضه بدون إضافة قواعد أمان لقاعدة بياناتك.
  6. انقر على إنشاء.

لنخصّص بعض الوقت لإنشاء قواعد أمان قوية في قاعدة بيانات Firestore.

  1. افتح لوحة بيانات Firestore وانتقِل إلى علامة التبويب القواعد.
  2. عدِّل قواعد الأمان لتظهر على النحو التالي:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

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

تشغيل التطبيق

أصبحت الآن جاهزًا لتشغيل التطبيق. افتح مجلد make-it-so-android/start في Android Studio وشغِّل التطبيق (يمكن إجراء ذلك باستخدام محاكي Android أو جهاز Android حقيقي).

3- مصادقة Firebase

ما هي الميزة التي ستضيفها؟

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

حان وقت الترميز

بعد أن ينشئ المستخدم حسابًا من خلال كتابة عنوان بريد إلكتروني وكلمة مرور، عليك طلب بيانات اعتماد بريد إلكتروني من Firebase Authentication API، ثم ربط بيانات الاعتماد الجديدة بالحساب المجهول الهوية. افتح ملف AccountServiceImpl.kt في "استوديو Android" وعدِّل الدالة linkAccount لتظهر على النحو التالي:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

الآن افتح SignUpViewModel.kt واستدِع دالة الخدمة linkAccount داخل العنصر launchCatching لدالة onSignUpClick:

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

يحاول أولاً المصادقة، وإذا نجحت المكالمة، ينتقل إلى الشاشة التالية (SettingsScreen). أثناء تنفيذ هذه المكالمات داخل كتلة launchCatching، إذا حدث خطأ في السطر الأول، سيتمّ رصد الاستثناء ومعالجته، ولن يتمّ الوصول إلى السطر الثاني على الإطلاق.

بعد فتح SettingsScreen مرة أخرى، عليك التأكّد من أنّ خيارَي تسجيل الدخول وإنشاء حساب قد اختفيا، لأنّه تمّت مصادقة المستخدم الآن. لإجراء ذلك، لنجعل SettingsViewModel يستمع إلى حالة المستخدم الحالي (متاحة في AccountService.kt) للتحقّق مما إذا كان الحساب مجهول الهوية أم لا. لإجراء ذلك، عدِّل uiState في SettingsViewModel.kt لتظهر على النحو التالي:

screens/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

آخر إجراء عليك اتّخاذه هو تعديل uiState في SettingsScreen.kt لجمع الحالات التي تطلقها SettingsViewModel:

screens/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

الآن، في كل مرة يتغيّر فيها المستخدم، ستعيد SettingsScreen إعادة تكوين نفسها لعرض الخيارات وفقًا لحالة المصادقة الجديدة للمستخدم.

حان وقت الاختبار

شغِّل تطبيق Make it So وانتقِل إلى الإعدادات من خلال النقر على رمز الترس في أعلى يسار الشاشة. بعد ذلك، انقر على خيار إنشاء الحساب:

شاشة إعدادات تطبيق Make it So شاشة الاشتراك في تطبيق Make it So

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

4. Cloud Firestore

ما هي الميزة التي ستضيفها؟

بالنسبة إلى Cloud Firestore، ستضيف مستمعًا إلى مجموعة Firestore التي تخزِّن المستندات التي تمثّل المهام المعروضة في Make it So. بعد إضافة هذا المستمع، ستتلقّى كل تعديل يتم إجراؤه على هذه المجموعة.

حان وقت الترميز

عدِّل Flow المتاح في StorageServiceImpl.kt ليصبح على النحو التالي:

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

تضيف هذه التعليمة البرمجية مستمعًا إلى مجموعة المهام استنادًا إلى user.id. يتم تمثيل كل مهمة من خلال مستند في مجموعة باسم tasks، ويحتوي كل مستند على حقل باسم userId. يُرجى العِلم أنّه سيتم إصدار Flow جديد إذا تغيّرت حالة currentUser (عن طريق تسجيل الخروج مثلاً).

عليك الآن ضبط Flow في TasksViewModel.kt على القيمة نفسها في الخدمة:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

وسيكون الإجراء الأخير هو جعل composable function في TasksScreens.kt، الذي يمثّل واجهة المستخدم، على دراية بهذا المسار وجمعه كحالة. في كل مرة تتغيّر فيها الحالة، ستعيد الدالة القابلة للتجميع تكوين نفسها تلقائيًا وتعرض أحدث حالة للمستخدم. أضِف ما يلي إلى TasksScreen composable function:

screens/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

بعد أن تتمكّن الدالة القابلة للتجميع من الوصول إلى هذه الحالات، يمكنك تعديل LazyColumn (وهي البنية التي تستخدمها لعرض قائمة على الشاشة) لتبدو على النحو التالي:

screens/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

حان وقت الاختبار

لاختبار نجاح ذلك، أضِف مهمة جديدة باستخدام التطبيق (من خلال النقر على زر الإضافة في أسفل يسار الشاشة). بعد الانتهاء من إنشاء المهمة، من المفترض أن تظهر في مجموعة Firestore في وحدة تحكّم Firestore. إذا سجّلت الدخول إلى تطبيق Make it So على أجهزة أخرى باستخدام الحساب نفسه، ستتمكّن من تعديل المهام التي تريد إنجازها ومشاهدة تعديلاتها على جميع الأجهزة في الوقت الفعلي.

5- مراقبة الأداء

ما هي الميزة التي ستضيفها؟

يُعدّ الأداء من الأمور المهمة جدًا التي يجب الانتباه إليها، لأنّه من المرجّح أن يتوقف المستخدمون عن استخدام تطبيقك إذا لم يكن الأداء جيدًا واستغرقوا وقتًا طويلاً لإكمال مهمة بسيطة باستخدامه. لهذا السبب، من المفيد أحيانًا جمع بعض المقاييس عن رحلة معيّنة يقطعها المستخدِم في تطبيقك. لمساعدتك في ذلك، تقدّم ميزة "مراقبة الأداء" في Firebase عمليات تتبُّع مخصّصة. اتّبِع الخطوات التالية لإضافة عمليات تتبُّع مخصّصة وقياس الأداء في أجزاء مختلفة من الرمز في Make it So.

حان وقت الترميز

إذا فتحت ملف Performance.kt، سترى دالة مضمّنة تُسمى trace. تستدعي هذه الدالة Performance Monitoring API لإنشاء تتبع مخصّص، مع تمرير اسم التتبُّع كمَعلمة. المعلمة الأخرى التي تظهر لك هي مجموعة الرموز البرمجية التي تريد مراقبتها. المقياس التلقائي الذي يتم جمعه لكلّ عملية تتبُّع هو الوقت الذي يستغرقه تنفيذها بالكامل:

model/service/Performance.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

يمكنك اختيار أجزاء قاعدة البيانات التي تعتقد أنّه من المهم قياسها وإضافة عمليات تتبُّع مخصّصة إليها. في ما يلي مثال على إضافة تتبع مخصّص إلى الدالة linkAccount التي رأيتها سابقًا (في AccountServiceImpl.kt) في هذا الدليل التعليمي للترميز:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

حان دورك الآن. أضِف بعض عمليات التتبّع المخصّصة إلى تطبيق Make it So وانتقِل إلى القسم التالي لاختبار ما إذا كان يعمل على النحو المتوقّع.

حان وقت الاختبار

بعد الانتهاء من إضافة عمليات التتبّع المخصّصة، شغِّل التطبيق وتأكَّد من استخدام الميزات التي تريد قياسها بضع مرّات. بعد ذلك، انتقِل إلى وحدة تحكُّم Firebase ثم إلى لوحة بيانات الأداء. في أسفل الشاشة، ستظهر لك ثلاث علامات تبويب: طلبات الشبكة وعمليات التتبّع المخصّصة وعرض الشاشة.

انتقِل إلى علامة التبويب عمليات التتبّع المخصّصة وتأكَّد من أنّ عمليات التتبّع التي أضفتها في قاعدة الرموز البرمجية معروضة هناك، وأنّه يمكنك معرفة الوقت الذي يستغرقه عادةً تنفيذ أجزاء الرمز البرمجي هذه.

6- الإعداد عن بُعد

ما هي الميزة التي ستضيفها؟

هناك العديد من حالات الاستخدام لميزة "الإعداد عن بُعد"، بدءًا من تغيير مظهر تطبيقك عن بُعد ووصولاً إلى ضبط سلوكيات مختلفة لشرائح المستخدمين المختلفة. في هذا الدليل التعليمي حول رموز البرامج، ستستخدم ميزة "الإعداد عن بُعد" لإنشاء زرّ تبديل للميزات يعرض ميزة تعديل المهمة الجديدة أو يخفيها في تطبيق Make it So.

حان وقت الترميز

أول ما عليك فعله هو إنشاء الإعدادات في وحدة تحكّم Firebase. لإجراء ذلك، عليك الانتقال إلى لوحة بيانات "الإعداد عن بُعد" والنقر على الزر إضافة مَعلمة. املأ الحقول وفقًا للصورة أدناه:

مربّع الحوار &quot;إنشاء مَعلمة&quot; في ميزة &quot;الإعداد عن بُعد&quot;

بعد ملء جميع الحقول، يمكنك النقر على الزر حفظ ثم نشر. بعد إنشاء المَعلمة وإتاحتها لقاعدة الرموز البرمجية، عليك إضافة الرمز الذي سيجلب القيم الجديدة إلى تطبيقك. افتح ملف ConfigurationServiceImpl.kt وعدِّل تنفيذ هاتين الدالتَين:

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

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

تعرض الدالة الثانية القيمة المنطقية التي تم نشرها للمَعلمة التي أنشأتها للتو في "وحدة التحكّم". وستحتاج إلى استرداد هذه المعلومات في TasksViewModel.kt، عن طريق إضافة ما يلي إلى الدالة loadTaskOptions:

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

يتم استرداد القيمة في السطر الأول واستخدامها لتحميل خيارات القائمة لعناصر المهام في السطر الثاني. إذا كانت القيمة false، يعني ذلك أنّ القائمة لن تحتوي على خيار التعديل. بعد أن أصبحت لديك قائمة الخيارات، عليك جعل واجهة المستخدم تعرِضها بشكل صحيح. أثناء إنشاء تطبيق باستخدام Jetpack Compose، عليك البحث عن composable function التي تحدّد شكل واجهة مستخدم TasksScreen. لذلك، افتح ملف TasksScreen.kt وعدِّل LazyColum للإشارة إلى الخيارات المتاحة في TasksViewModel.kt:

screens/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

TaskItem هو composable function آخر يوضّح الشكل الذي يجب أن يظهر به واجهة المستخدم لمهمة واحدة. تحتوي كل مهمة على قائمة بخيارات يتم عرضها عندما ينقر المستخدم على رمز النقاط الثلاث في نهايتها.

حان وقت الاختبار

أصبح بإمكانك الآن تشغيل التطبيق. تأكَّد من أنّ القيمة التي نشرتها باستخدام وحدة تحكّم Firebase تتطابق مع سلوك التطبيق:

  • إذا كان false، من المفترض أن يظهر لك خياران فقط عند النقر على رمز النقاط الثلاث:
  • إذا كان true، من المفترض أن تظهر لك ثلاثة خيارات عند النقر على رمز النقاط الثلاث:

جرِّب تغيير القيمة عدة مرات في وحدة التحكّم وإعادة تشغيل التطبيق. بهذه السهولة يمكنك إطلاق ميزات جديدة في تطبيقك باستخدام أداة "الإعداد عن بُعد".

7- تهانينا

تهانينا، لقد نجحت في إنشاء تطبيق Android باستخدام Firebase وJetpack Compose.

لقد أضفت "مصادقة Firebase" و"مراقبة الأداء" و"الإعداد عن بُعد" وCloud Firestore إلى تطبيق Android تم إنشاؤه بالكامل باستخدام Jetpack Compose لواجهة المستخدم، وجعلته متوافقًا مع بنية MVVM المقترَحة.

مراجع إضافية

مستندات المرجع