Router — أساسيات نمط التوجيه في iOS وAndroid

المؤلف: IT Sectr نُشر: 2026-02-19 وقت القراءة: 9 دق

Router (الموجه) — نمط معماري يركز منطق التنقل بين شاشات التطبيق. يحدد Router إلى أين وكيف ينتقل عند حدوث حدث. في iOS، يقوم Router بتجريد UINavigationController وsegue؛ في Android — FragmentManager وNavController من Jetpack Navigation. يمكن أن يعمل Router كنمط مستقل أو كمكون من Coordinator (Router-per-Coordinator) وVIPER (Router-per-module). المزيد من التفاصيل — في Android Navigation Component Guide.

النقاط الرئيسية

  • Router — نمط توجيه يركز التنقل بين الشاشات
  • مستويان — يمكن أن يكون Router مكونًا من Coordinator أو نمطًا مستقلاً
  • iOS Router — تجريد فوق UINavigationController مع طرق push/present/pop
  • Android Router — Jetpack Navigation Component مع NavController وdeep links
  • URL Routing — التوجيه بناءً على مخططات URL (deeplink, universal link)

ما هو Router: جوهر نمط التوجيه

Router — نمط يغلف منطق الانتقال بين الشاشات. الفكرة الرئيسية: ViewController (أو Presenter/ViewModel) لا يستدعي navigationController.pushViewController مباشرة، بل يخطر Router بنية التنقل. يقرر Router أي ViewController سينشئ وكيف يعرضه. يمكن أن يكون Router بسيطًا (مجموعة من طرق التنقل) أو معقدًا (مع التوجيه بمخططات URL وdeep links وإشعارات الدفع).

ثلاث طرق لـ Router — Router كخدمة (Navigator singleton عام)، Router كمكون وحدة (طريقة VIPER)، Router كبروتوكول (تجريد لـ Coordinator). Router العام — البساطة (مثيل واحد لكل تطبيق) — شائع في المشاريع الصغيرة. Router المعياري — كل feature لها Router خاص بها — معيار VIPER وClean Architecture. Router البروتوكولي — يُستخدم في Coordinator: RouterProtocol مع طرق push/present/pop/dismiss.

الطريقةالهيكلمتى يُستخدم
Navigator العامSingleton مع طرق تنقلالمشاريع الصغيرة، النماذج الأولية
Router المعياريRouter داخل كل وحدةVIPER, Clean Architecture
Router البروتوكوليبروتوكول + تنفيذ لـ CoordinatorMVVM-C, Coordinator pattern

تاريخ النمط — ظهر Router في أطر الويب (URL Router — Rails Routes, Express.js) وتم تكييفه لتطوير الأجهزة المحمولة. في iOS، أصبح Router شائعًا مع صعود VIPER (2015-2017). في Android، Router جزء من Jetpack Navigation (2018)، حل Google القياسي للتنقل. اليوم، Router جزء لا يتجزأ من أي بنية معيارية لتطبيقات الأجهزة المحمولة.

Router في iOS: التنفيذ بلغة Swift

iOS Router — بروتوكول مع طرق لأنواع مختلفة من التنقل. التنفيذ الأساسي يغلف UINavigationController. يمكن لـ Router التعامل مع العروض التقديمية المشروطة (present/dismiss)، وpush/pop في مكدس التنقل، وshowDetail لـ Split View Controller، والعروض التقديمية بملء الشاشة (iOS 13+). Router لا ينشئ ViewControllers — يتلقى مثيلاً جاهزًا ويعرضه. إنشاء ViewControllers يتم بواسطة مصنع (Assembly, حاوية DI) أو Coordinator.

swift
// RouterProtocol لنظام iOS
protocol RouterProtocol {
    func push(_ viewController: UIViewController, animated: Bool)
    func pop(animated: Bool)
    func popToRoot(animated: Bool)
    func present(_ viewController: UIViewController, animated: Bool)
    func dismiss(animated: Bool)
    func setViewControllers(_: [UIViewController], animated: Bool)
}

// تنفيذ Router عبر UINavigationController
class NavigationRouter: RouterProtocol {
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func push(_ vc: UIViewController, animated: Bool) {
        if navigationController.presentedViewController {
            navigationController.dismiss(animated: false)
        }
        navigationController.pushViewController(vc, animated: animated)
    }

    func pop(animated: Bool) {
        navigationController.popViewController(animated: animated)
    }

    func popToRoot(animated: Bool) {
        navigationController.popToRootViewController(animated: animated)
    }

    func present(_ vc: UIViewController, animated: Bool) {
        navigationController.present(vc, animated: animated)
    }

    func dismiss(animated: Bool) {
        navigationController.dismiss(animated: animated)
    }

    func setViewControllers(_ vcs: [UIViewController], animated: Bool) {
        navigationController.setViewControllers(vcs, animated: animated)
    }
}

// Assembly — إنشاء وحدة مع Router
protocol ProfileModuleFactory {
    func makeProfileView() -> UIViewController
}

Router في VIPER — كل وحدة VIPER لها Router خاص بها، يعرف أي الشاشات يفتح من الوحدة الحالية. ينشئ Router ViewController للوحدة التالية من خلال مصنع ويسلم التحكم. Router في VIPER هو المكون الوحيد الذي يحتوي على import UIKit (باستثناء View). هذا يبسط اختبار Presenter وInteractor: لا يعتمدان على UIKit.

Router في Android: Jetpack Navigation Component

Jetpack Navigation Component — جهاز التوجيه القياسي لـ Android، الذي أوصت به Google منذ 2018. يتم تعريف التنقل في رسم بياني XML (nav_graph.xml): العقد (fragments, activities, destinations) والحواف (actions). يدير NavController الانتقالات: يستدعي FragmentManager.replace/add ويدير مكدس الرجوع. يدعم Navigation Component deep links والوسائط ورسوم الانتقالات وSafe Args لنقل البيانات بشكل type-safe.

kotlin
// nav_graph.xml — رسم بياني للتنقل
<!-- @xml/nav_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph"
    app:startDestination="@id/loginFragment">

    <fragment android:id="@+id/loginFragment"
        android:name=".ui.login.LoginFragment">
        <action android:id="@+id/to_home"
            app:destination="@id/homeFragment" />
    </fragment>

    <fragment android:id="@+id/homeFragment"
        android:name=".ui.home.HomeFragment">
        <argument android:name="userId"
            android:defaultValue="0"
            app:argType="integer" />
        <deepLink app:uri="myapp://home/{userId}" />
    </fragment>
</navigation>

// Kotlin — التنقل عبر NavController
class LoginFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.loginButton.setOnClickListener {
            val action = LoginFragmentDirections.toHome(userId = 42)
            findNavController().navigate(action)
        }
    }
}

// Safe Args — وسائط type-safe
class HomeFragmentArgs : NavArgs {
    val userId: Int get() = arguments?.getInt("userId") ?: 0
}

// NavigationUI — التكامل مع Toolbar وBottomNav وDrawer
class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val navHost = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHost.navController
        NavigationUI.setupActionBarWithNavController(this, navController)
    }
}

Deep links في Android Navigation — يدعم Navigation Component deep links الصريحة (PendingIntent + NavDeepLinkBuilder) والضمنية (عبر intent-filter في البيان). يمكن أن يؤدي deep link إلى أي عقدة في nav_graph. معالجة إشعارات الدفع: ينشئ Notification PendingIntent مع NavDeepLinkBuilder، الذي يستعيد مكدس الرجوع ويفتح الشاشة المطلوبة. يتعامل Navigation Component تلقائيًا مع أزرار Up/Back ويعيد المستخدم إلى الشاشة السابقة.

URL Routing: توجيه Deeplink وUniversal Link

URL Routing — التوجيه بناءً على مخططات URL وdeep links وuniversal links. يسجل التطبيق مخططًا (myapp://profile/42) أو universal link (https://example.com/profile/42). يحلل Router عنوان URL، ويستخرج المعلمات (userId=42)، ويفتح الشاشة المقابلة. يتيح URL Routing فتح التطبيق من الخارج: عبر رابط من موقع ويب أو بريد إلكتروني أو إشعار دفع أو رمز QR. يستخدم iOS NSUserActivity + universal link، Android — intent-filter + deep link.

swift
// URL Router — تحليل deep link والتنقل
protocol URLRoute {
    var pattern: String { get }
    func navigate(parameters: [String: String], router: RouterProtocol)
}

struct ProfileRoute: URLRoute {
    let pattern = "myapp://profile/{userId}"

    func navigate(parameters: [String: String], router: RouterProtocol) {
        guard let userId = parameters["userId"] else { return }
        let profileVC = ProfileViewController(userId: userId)
        router.push(profileVC, animated: true)
    }
}

// URL Router — مطابقة URLs مع المسارات
class URLRouter {
    private var routes: [URLRoute] = []

    func handle(_ url: URL, router: RouterProtocol) {
        for route in routes {
            if let params = matchPattern(route.pattern, url: url) {
                route.navigate(parameters: params, router: router)
                return
            }
        }
    }
}

توجيه deep link عمليًا — يجب أن يعالج التطبيق deep links في ثلاث حالات: غير مشغل (بدء التشغيل مع deep link)، في الخلفية (الاستعادة من الخلفية)، نشط (onNewIntent/SceneDelegate). يجب على URL Router استعادة مكدس الرجوع بشكل صحيح: عند deep link إلى شاشة ملف شخصي، يجب أن يتمكن المستخدم من الضغط على رجوع والعودة إلى الشاشة السابقة. iOS وAndroid يتعاملان مع مكدسات الرجوع بشكل مختلف — يجب على Router مراعاة خصائص المنصة.

مقارنة Router مع Coordinator وNavigator

Router vs Coordinator — Router يجيب على السؤال "كيفية عرض شاشة؟" (push/present)، Coordinator — "أي شاشة عرض؟" (أي تدفق يبدأ). Router أداة تنقل، Coordinator منظم تدفق. يستخدم Coordinator Router. في VIPER، يؤدي Router كلا الوظيفتين: يقرر إلى أين يذهب وكيف يعرضها. في MVVM-C، Router بروتوكول منفصل يستخدمه Coordinator. الأنماط لا تستبعد بل تكمل بعضها البعض.

الخاصيةRouterCoordinatorNavigator
التركيزآلية التنقلتدفق التنقلوصول عام
المستوىإطار UI (push/present)منطق الأعمال (تدفق)بنية تحتية
إنشاء الشاشاتيستقبل VC جاهزًاينشئ VC عبر مصنعينشئ VC
دورة الحياةواحد لكل تنقلchildCoordinatorsSingleton

Router vs Navigator — Navigator هو اسم أقدم لـ Router في مجتمع iOS. في التطبيقات المبكرة، كان Navigator فئة Singleton مع طرق تنقل. Navigator هو Router singleton عام. Navigator أبسط لكنه يخلق تبعيات خفية. Router القائم على البروتوكول أكثر قابلية للاختبار ونمطية. Navigator مناسب للمشاريع بدون بنية صارمة، Router — لـ Clean Architecture وVIPER.

الأسئلة الشائعة

ما الفرق بين Router وCoordinator؟

Router هو تجريد فوق UINavigationController (push/present/pop). Coordinator هو طبقة فوق Router تدير التدفق (أي شاشة تظهر بعد ذلك). Router يتعامل مع الآلية، Coordinator يتعامل مع منطق التنقل. Coordinator يستخدم Router لتنفيذ الانتقالات. في VIPER، يجمع Router بين الوظيفتين؛ في MVVM-C، منفصلتان.

هل من الإلزامي استخدام Router في تطبيق محمول؟

لا. للتطبيقات ذات 3-5 شاشات، الاستدعاءات المباشرة لـ navigationController.pushViewController هي حل مقبول تمامًا. يصبح Router مفيدًا مع 10+ شاشات وdeep links وإشعارات الدفع والبنية المعيارية. Router يبسط اختبار التنقل ويسمح بمعالجة مركزية للانتقالات من أي مصدر (زر، deep link، إشعار دفع).

كيف يرتبط Router بـ Deep Links؟

URL Router هو امتداد لـ Router يحلل deep links وuniversal links. يحدد URL Router التطابق بين عناوين URL وشاشات التطبيق. على سبيل المثال، مسار myapp://profile/42 → ProfileViewController(userId: 42). URL Router يركز معالجة deep links من مصادر مختلفة: إشعارات الدفع ورموز QR وروابط البريد الإلكتروني والروابط العامة.

هل يمكن استخدام Router بدون Coordinator؟

نعم. يمكن لـ Router العمل بشكل مستقل — كـ Navigator عام أو كمكون وحدة VIPER. Router بدون Coordinator هو ببساطة تغليف UINavigationController خلف بروتوكول. Coordinator بدون Router أكثر تعقيدًا (ViewController يستدعي push/present مباشرة)، لكنه ممكن. أفضل ممارسة هي Router + Coordinator معًا.

هل Router في Android هو نفسه iOS Router؟

وظيفيًا — نعم، كلاهما يركزان التنقل. التنفيذ مختلف: iOS Router — بروتوكول + فئة حول UINavigationController؛ Android — Jetpack Navigation Component مع رسم بياني XML وNavController. Android Router مدمج في الإطار (Navigation Component)، iOS Router هو تنفيذ طرف ثالث. Android Router يدعم Safe Args، iOS Router لا (يحتاج generics أو Codable).

الملخص

  • Router — نمط توجيه يغلف منطق الانتقال بين الشاشات
  • iOS Router — بروتوكول مع طرق push/present/pop حول UINavigationController
  • Android Router — Jetpack Navigation Component مع XML nav_graph وNavController
  • URL Routing — التوجيه عبر deep links وuniversal links
  • Router vs Coordinator — Router يتعامل مع الآلية، Coordinator يتعامل مع تدفق التنقل
  • التطبيق — بنية معيارية، deep links، اختبار التنقل

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا