Skip to main content
  1. Posts/

Design Patterns for Mobile

·20 mins·

Mobile apps require specialized design patterns at both the UI and architecture levels to meet constraints like small screens, touch input, and diverse platforms. This article surveys common UI/UX patterns (navigation bars, drawers, cards, sheets, etc.) and software architecture patterns (MVC, MVP, MVVM, VIPER, Redux/Flux, etc.) in iOS, Android, and cross-platform development. For each pattern we define its purpose, list typical use cases, and analyze pros/cons and implementation tips, including code sketches in Swift, Kotlin, Flutter/Dart, or React Native. We emphasize platform conventions (e.g. iOS vs Android placement, official libraries like Android Jetpack or Apple UIKit/SwiftUI, Flutter/React Native best practices) and cite primary sources (platform docs, authoritative articles).

The report concludes with a comparison table summarizing pattern categories, complexity, performance impact, and when to use each. We also include diagrams: a Mermaid flowchart to help choose navigation patterns based on app requirements, and a component-layer diagram illustrating a typical layered mobile architecture (UI, domain, data layers).

Mobile UI patterns guide how users navigate and interact on small screens. We cover key patterns: navigation bars and drawers, card or list layouts, bottom sheets and dialogs, and common interactive patterns.

Bottom Navigation / Tab Bar
#

Definition & Use: A bottom navigation bar (iOS Tab Bar) displays 3–5 top-level destinations persistently at the bottom of the screen. Each tab shows an icon (and label) that takes the user to a main screen. For example, many social apps (Facebook, Twitter, Instagram) use a bottom tab bar for core sections (Home, Search, Profile, etc.). This pattern is ideal for handheld devices because the bottom is within easy thumb reach. Material Design specifies bottom nav for “three to five destinations of equal importance, on compact window sizes”.

Implementation Tips:

  • iOS: Use a UITabBarController (UIKit) or TabView (SwiftUI) with tabItem modifiers. Example (SwiftUI):
    TabView {
      HomeView().tabItem { Label("Home", systemImage: "house.fill") }
      SearchView().tabItem { Label("Search", systemImage: "magnifyingglass") }
      ProfileView().tabItem { Label("Me", systemImage: "person.fill") }
    }
    
  • Android: Use BottomNavigationView (XML/Compose) or Jetpack Compose NavigationBar/NavigationBarItem. For instance, Compose code:
    BottomNavigation {
        BottomNavigationItem(icon = { Icon(Icons.Default.Home) }, label = { Text("Home") }, ...)
        BottomNavigationItem(icon = { Icon(Icons.Default.Search) }, label = { Text("Search") }, ...)
    }
    
  • Cross-Platform: React Native apps often use libraries like React Navigation (createBottomTabNavigator); Flutter uses BottomNavigationBar widgets.

Pros:

  • Always visible and thumb-friendly.
  • Shows the current location at a glance via selected tab highlight.
  • Simple and direct switching between main sections (one tap).

Cons:

  • Limited slots: generally max 5 items; more and touch targets become cramped.
  • Android vs iOS differences: Material often favors bottom tabs, but traditional Android guidelines placed tabs at top (before Material bottom nav). iOS Tab Bar is bottom, while pre-Android’s hardware buttons made top tabs common. Ensure platform conventions (e.g. on Android modern apps now favor bottom nav, on iOS bottom tab is standard).
  • Not ideal for apps with many sections or deep hierarchies; best only for top-level navigation.

Real-world Examples:

  • Twitter (iOS, Android): 5 icon tabs (Home, Search, Post, Notifications, Messages) at bottom.
  • Instagram: 5 tabs (Home, Search, Create, Activity, Profile).
  • TikTok: 4 tabs (Home, Discover, Inbox, Profile).

These apps match the “few main destinations” guideline and use bottom nav for quick switching.

Navigation Drawer / Hamburger Menu#

Definition & Use: The navigation drawer (a.k.a. hamburger menu) slides in from the side and hides navigation options until needed. On Android it’s invoked by tapping the “hamburger” icon (☰) or swiping from the side; on iOS it’s much less common. This pattern suits apps with many or secondary destinations. For instance, Uber’s app reserves the main screen for ride-hailing and hides secondary options (Payment, History, Settings) behind a side menu.

When to Use:

  • Use when there are many features (beyond 5) that would overcrowd a bottom tab bar.
  • Or when you want a clean main UI (focusing on primary task) and can tuck other items away.
  • Not for key features: hiding important options harms discoverability.

Pros:

  • Space-efficient: accommodates many menu items off-screen.
  • Clean main UI: frees up screen for content (no nav buttons clutter).

Cons:

  • Low discoverability: users may not realize hidden items. As Smashing Mag notes, “what’s out of sight is out of mind”.
  • Additional tap: requires tapping the hamburger icon then the item (two taps to reach destination).
  • Platform clash: While nearly standard on Android (“Navigation Drawer” in Material Design), on iOS it clashes with built-in gestures and nav-bars. Apple’s HIG discourages this for primary nav.
  • Context hiding: it can obscure current location; unlike tabs, it offers no at-a-glance feedback.

Implementation Tips:

  • Android: Use DrawerLayout with NavigationView, or Jetpack Navigation component’s drawer. Material Design calls it a Navigation Drawer. Eg:
    <androidx.drawerlayout.widget.DrawerLayout ...>
        <FrameLayout android:id="@+id/content_frame" ... />
        <com.google.android.material.navigation.NavigationView ... app:menu="@menu/drawer_menu"/>
    </androidx.drawerlayout.widget.DrawerLayout>
    
  • iOS: Less common; can use third-party libraries (e.g. MMDrawerController), but prefer tab bars or other patterns per HIG. If used, ensure it does not conflict with swipe-back gestures.
  • Clearly prioritize menu items: keep most-used ones visible and hide only truly secondary features. Smashing suggests moving essential functions to a tab bar if possible.
  • Consider alternatives: if only a couple of extra items, maybe use a “More” tab or in-page menu instead.
  • Example: Google Maps on Android uses a drawer for settings; on iOS it uses a bottom drawer for location details (non-modal sheet).

Tab / Segmented Navigation
#

Definition & Use: Top tabs (segmented controls on iOS, TabLayout on Android) can be placed at the top of the screen (below app bar) to switch between a few closely related views. This is appropriate for 2–4 categories of similar weight (e.g. News / For You / Following), where you want quick context-switching but tabs at the bottom already used or not suitable. On iOS, the UISegmentedControl or Picker can serve; on Android TabLayout/TabStrip. Use for sub-navigation within a section.

Platform Notes: Historically Android placed tabs at top (often with swipeable pages), whereas iOS sometimes uses segmented controls at top of content (e.g. iOS Music app uses “Library/For You/New”). If you have a bottom tab bar, avoid also having a top tab bar – that can confuse users. Material guidelines advise against mixing tab bars with bottom nav for same content.

Pros:

  • Good for segmenting a page into sub-sections (e.g. categories of feeds).
  • All options visible, immediate feedback of active tab.

Cons:

  • Still limited options (usually max 3-4); too many tabs overflow or require scrolling (poor UX).
  • Consumes vertical space (less content area).
  • Requires careful labeling for clarity.

Implementation:

  • Android: Use TabLayout with ViewPager, or Compose’s TabRow.
  • iOS/SwiftUI: Use SegmentedPickerStyle:
    Picker("", selection: $segmentIndex) {
      Text("Favorites").tag(0)
      Text("Recents").tag(1)
    }
    .pickerStyle(SegmentedPickerStyle())
    
  • React Native: use segmented control component or libraries like React Navigation’s TopTabNavigator.

Real-world Example: In the Apple Music app, “Library” is segmented into “Playlists / Artists / Songs”, using a segmented control.

Card/List Layouts
#

Definition & Use: Card-based layouts present individual content chunks or items as “cards” – self-contained boxes showing an image or icon, title, and brief details. Popularized by Material Design (“Cards display content and actions about a single subject” and used in news feeds, social media, e-commerce (product cards), etc. Cards make lists scannable and visually organized. Common mobile apps (Facebook, Twitter timeline, Apple News) use scrollable lists of cards.

Pros:

  • Engaging presentation: Images, text, and actions grouped make content attractive.
  • Flexible: Can be arranged in lists or grids (responsive on tablets vs phones).
  • Intuitive: Users know to tap a card to drill into detail (the card hints at more info).
  • Cards often have a primary action (like tap), and optionally secondary actions (like share button).

Cons:

  • Overload risk: If too many dense cards or poor design, users may be overwhelmed (each card must be simple).
  • Performance: Long lists of rich cards (images/video) can increase memory and network load (lazy loading needed).

Implementation Tips:

  • Use recycled views (RecyclerView on Android, UITableView/UICollectionView on iOS) to list cards.
  • On iOS SwiftUI, use List or ScrollView with LazyVStack and custom card views.
  • On Android, use RecyclerView or Compose’s LazyColumn/LazyRow with Card composable (Material).
  • Styling: Follow Material or HIG card style. For example, Material provides CardView (Android XML) or Card (Compose).
  • Include clear call-to-action (CTA) if needed (e.g. a “Buy” button on product card).

Example: Google News shows each article as a card (image + headline). Tinder uses full-screen cards for profiles. Each card leads to a detail page.

Infinite Scroll vs Pagination
#

Definition & Use: Infinite scroll loads more content as the user scrolls (endless feed), common in social feeds (Facebook, Twitter). Pagination (with “Load more” button or page dots) is an alternative when you want explicit control. Use infinite scroll for content-heavy apps where users expect constant browsing (e.g. news, social). Use pagination (or segmented scrolling) when data is naturally chunked (e.g. a multi-step form or setup wizard).

Pros of Infinite Scroll:

  • Seamless browsing: no taps to new pages, keeps engagement.
  • Good for discovery (users keep finding content).

Cons:

  • Hard to reach footer elements (never a “bottom”).
  • Users can lose context (“how far did I scroll?”).
  • Performance: must manage loading states and potentially large data sets (use background loading, caching).
  • Not suitable if users need to reach page end (like a checkout summary).

Implementation Tips:

  • Detect scroll-to-bottom and trigger loading next batch. On Android, add RecyclerView.OnScrollListener; on iOS detect tableView(_:willDisplay:forRowAt:); in Compose use rememberLazyListState to check visible items.
  • Show a loading spinner at bottom during fetch.
  • Cap list size or allow jumping back (virtualized lists).
  • If data is static or small, prefer pagination (easier navigation).

Search and Filter UI
#

Definition & Use: Most content-driven apps include search bars and filter/sort menus. A search bar (often at the top, sometimes hidden until tapped) allows quick lookup. Filters (toggles, checkboxes, segmented controls) let users refine large result sets.

Implementation:

  • Android: Use SearchView (in the toolbar) or a TextField with onSubmit. Material Design has a search dialog or a persistent top bar search.
  • iOS: Use UISearchBar in a UITableView header or searchable modifier in SwiftUI (iOS 15+). Filters often use UISegmentedControl or a modal view.
  • Provide immediate feedback (live filtering) if possible, or a “Search” button on the keyboard.
  • For large data, consider backend search or local caching.

Tips:

  • Keep search prominent if key. If rarely used, tuck it behind a search icon or separate screen.
  • Always enable easy dismissal (clear button, Cancel).

Bottom Sheets & Modals
#

Definition & Use: Bottom sheets (Material) or action sheets/modals (iOS) are UI surfaces that slide up from the bottom to show supplementary content or actions. Two main types: non-modal (user can still see background and tap it) and modal (forces user to act or close it). Use non-modal bottom sheets for showing extra info while keeping context (e.g. Google Maps place info. Use modal sheets for critical tasks that require focus (e.g. file picker, login).

Pros:

  • Provide contextually-relevant info or actions without leaving current screen.
  • Natural on mobile (finger gestures to pull up a panel).

Cons & Cautions:

  • Hard to dismiss if not designed well. Include clear close controls.
  • Can confuse navigation if used like full pages. Avoid stacking multiple sheets.
  • Don’t use for lengthy content or app’s “main flow”.

Implementation:

  • Android: Use BottomSheetDialog or ModalBottomSheetLayout in Compose. Material supports Modal Bottom Sheet and Persistent Bottom Sheet.
  • iOS: Use UISheetPresentationController (iOS 15+) or custom presentation. SwiftUI offers .sheet modifier for full-screen modals.
  • Include grab handle or close button; allow swipe-down to dismiss if non-critical.

Examples:

  • Google Maps: pulls up a small bottom sheet with location info that can expand.
  • Apple Maps: place cards expand into detailed sheets.
  • Many music/podcast apps show a mini-player at bottom that expands to full player (Apple Music, Spotify).

Summary of UI Pattern Trade-offs
#

  • Tabbed Bottom Navigation: Use for few top sections (1-5). Pros: always visible, simple. Cons: limited slots.
  • Drawer/Hamburger: Use for many sections or non-critical items. Pros: hides complexity. Cons: discoverability, extra tap.
  • Top Tabs/Segmented: Use for small set (2-4) of equal features. Pros: quick switch. Cons: eats space, limited.
  • Cards & Lists: Use for content feeds. Pros: engaging, flexible. Cons: performance heavy if not optimized.
  • Infinite Scroll: Use for content-heavy feeds. Pros: seamless. Cons: context loss, memory.
  • Sheets/Modals: Use for contextual actions or info. Pros: keeps context. Cons: can confuse navigation if misused.

Mobile Architecture Patterns
#

At the code level, mobile apps rely on architecture patterns to structure code for maintainability, testability, and platform adaptation. Common patterns include MVC, MVP, MVVM, VIPER, Redux/Flux, and newer hybrids (MVI, BLoC). We define each and discuss use cases and trade-offs, including relevant platform APIs or frameworks.

Model-View-Controller (MVC)
#

Definition: MVC is the classic pattern where the Model holds data/business logic, the View handles UI, and the Controller mediates between them. Apple’s older iOS frameworks (UIKit/AppKit) were built around MVC.

Use Cases: Simple apps where clear layer separation helps. Each controller responds to UI events, updates models, and refreshes the view.

Pros:

  • Clear conceptual separation (in principle).
  • No extra libraries needed; supported by frameworks (UIKit UIViewController roles as controller).

Cons:

  • Tendency for “Massive View Controllers”: on iOS, view controllers often grow huge because a lot of logic ends up in them. This hurts maintainability.
  • In Android, early apps often put logic in Activity, causing similar problems.
  • Harder to unit-test since controllers often directly tied to UI.

Platform Notes: iOS’s Cocoa Touch (pre-SwiftUI) explicitly uses MVC. Apple documentation states MVC yields reusable, extensible code, but warns controllers can bloated. Android’s older samples implicitly used Activity=Controller.

Implementation Tips: Keep controllers as slim as possible:

  • Delegate business logic to model layer or separate classes.
  • Let models (data layer) handle data fetching and processing, and Views just display.
  • In UIKit, minimize code in UIViewController viewDidLoad/viewWillAppear.

Model-View-Presenter (MVP)
#

Definition: MVP splits responsibilities into Model, View, and Presenter. The Presenter contains UI logic and interacts with the View via an interface (decoupling). Essentially, the View handles rendering and user input, the Model holds data, and the Presenter acts as a middle-man. This pattern was popular in Android and Windows development.

Use Cases: Android apps (especially pre-Architecture Components) or .NET WinForms apps. Use MVP when you need better testability than MVC or when you want to fully separate Android-specific Views from business logic.

Pros:

  • Testability: The Presenter is plain code (no Android SDK dependencies), so it’s easy to unit-test.
  • Clear separation: View (Activity/Fragment) delegates almost all logic to Presenter.
  • Prevents massive controllers because logic goes into Presenter, not Activity.
  • Many developers found MVP more structured for complex apps.

Cons:

  • Boilerplate: Requires writing interfaces/contracts between View and Presenter.
  • One-to-one coupling: usually one Presenter per screen; more classes to manage.
  • Still can have heavy code if not disciplined.

Implementation Tips:

  • Define a Contract interface: e.g. interface View { showData(); }, interface Presenter { loadData(); }.
  • The View (Activity) holds a reference to Presenter and calls presenter.loadData() on actions.
  • The Presenter calls model/data and then view.showData(result).
  • Android example (Kotlin):
    interface MainView { fun displayResult(data: String) }
    class MainPresenter(val view: MainView, val repo: DataRepo) {
        fun onLoad() {
           repo.getData { data -> view.displayResult(data) }
        }
    }
    
  • In Activity implement MainView and create Presenter(view=this).
  • Many Android devs moved to MVVM as Jetpack evolved, but MVP remains viable especially when ViewModel isn’t used.

Model-View-ViewModel (MVVM)
#

Definition: MVVM separates data (Model), UI rendering (View), and introduces a ViewModel that holds UI state and logic. The View observes the ViewModel, often via data binding or reactive streams. Changes in the ViewModel automatically update the View.

Use Cases: Modern iOS (SwiftUI), Android (Jetpack ViewModel), Flutter, React Native – any framework that supports binding or reactive UI. MVVM is now widely recommended: for Android Google’s documentation encourages ViewModel classes to handle logic, and Flutter’s official guide uses an MVVM-like split.

Pros:

  • Testable: ViewModels contain logic only and can be unit-tested independently.
  • Decouples UI: The View only binds to ViewModel; business logic/data remains separate. E.g., a ChartViewModel prepares data for display.
  • Respects single responsibility: Views just render and forward UI events; ViewModels manage state transformations.
  • Works well with data-binding: SwiftUI and Compose are essentially MVVM under the hood.

Cons:

  • Complexity: More moving parts than MVC.
  • Overhead: Writing ViewModel classes and setting up data flows can feel boilerplate if app is small.
  • Platform specifics: Without data-binding, need manual subscriptions (e.g. LiveData on Android).

Implementation Tips:

  • iOS/SwiftUI: Use @StateObject/@ObservedObject and Combine/ObservableObject. Example:
    class MyViewModel: ObservableObject {
      @Published var items: [Item] = []
      func loadData() { ... }
    }
    struct ContentView: View {
      @StateObject var vm = MyViewModel()
      var body: some View {
        List(vm.items) { item in Text(item.title) }
          .onAppear { vm.loadData() }
      }
    }
    
  • Android: Use ViewModel class (Jetpack) with LiveData or StateFlow for UI state. Eg:
    class MyViewModel : ViewModel() {
      val items: LiveData<List<Item>> = liveData { emit(repository.loadItems()) }
    }
    
  • Bind Views to ViewModel outputs. In Compose, use val items by viewModel.items.collectAsState().
  • Flutter: Flutter’s docs explicitly recommend an MVVM-ish structure: “Views and view models make up the UI layer…Repositories and services represent the data/model layer”. Use providers or state management for ViewModel (ChangeNotifier) to hold UI state.
  • React Native: While React follows MVCish, hooks like useReducer or libraries like MobX can implement MVVM concepts.

Primary Source: The official Flutter architecture guide describes an MVVM approach: “MVVM is an architectural pattern that separates a feature into Model, ViewModel, and View. Views and view models make up the UI layer of an application.” And Google’s Android guide emphasizes keeping UI logic in ViewModels. Microsoft likewise says MVVM separates UI from logic, aiding testability.

VIPER (iOS Clean Architecture)
#

Definition: VIPER is a layered architecture for iOS apps, applying Uncle Bob’s Clean Architecture principles. VIPER stands for View, Interactor, Presenter, Entity, Routing. It breaks an app into distinct parts: the Presenter handles UI logic, the Interactor has use-case/business logic, Entities are basic data models, and the Router (or Wireframe) handles navigation. Essentially, it enforces single-responsibility throughout the stack.

Use Cases: Large, complex iOS apps where maintainability, modularity, and testability are critical. Apps with many screens and business rules (banking, enterprise apps). VIPER gained traction as a remedy to MVC’s “Massive View Controller” problem.

Pros:

  • Modular & Testable: Each layer has a focused role. Interactors (use-cases) can be tested in isolation, as can Presenters and Routers.
  • Thin Controllers: View Controllers become “view” only; almost all logic moves elsewhere.
  • Single Responsibility: Components like Interactors encapsulate one use case each.
  • Clean Navigation: The Router explicitly manages screens flow.
  • VIPER “makes it easier to isolate dependencies and to test interactions at boundaries”.

Cons:

  • High complexity/boilerplate: Many files per screen (View, Presenter, Interactor, etc.) can be daunting for small teams or apps.
  • Learning curve: Developers must grasp multiple components and patterns.
  • Overkill for simple apps – better for scale.

Implementation Tips:

  • Structure code so each VIPER module folder has: ViewController (View), Presenter, Interactor, Entity (model objects), Router (or Wireframe).
  • Example (pseudocode):
    • ViewController implements HomeView protocol, sends user actions to HomePresenter.
    • HomePresenter has reference to HomeInteractor, updates View.
    • HomeInteractor executes business logic (calls services, database), returns data to Presenter.
    • Entity are simple structs/models used by Interactor.
    • Router creates View, Presenter, etc., and handles navigation to next screens.
  • Use protocols to decouple (e.g. InteractorOutput, ViewInput).
  • Reference: objc.io’s VIPER article is authoritative on structure.

Real-world Analogy: VIPER is essentially Clean Architecture for iOS. It came about because most iOS apps use MVC and suffered “Massive View Controllers”. VIPER introduces extra layers so logic isn’t shoved into controllers. For example, a to-do list app in VIPER would have one Interactor per use-case (e.g. “FetchTodos”, “AddTodo”), each testable separately.

Redux / Flux (Unidirectional Data Flow)
#

Definition: Originating from Facebook, Flux/Redux isn’t tied to any language but is widely used in React (and React Native) apps. It enforces a unidirectional data flow: a single store holds the app state, views dispatch actions, and reducers (pure functions) update the store based on actions. React UI components subscribe to store changes. Redux formalizes this: one global state, actions, and pure reducers.

Use Cases: Cross-platform or React-based apps with complex, shared state (multiple components depend on the same data). Examples: e-commerce (cart state), collaborative apps (feeds, messaging), or any app needing time-travel debug capabilities. Flutter also has Redux libraries (flutter_redux), and similar patterns like BLoC.

Pros:

  • Predictable state management: All state changes are traceable through actions (good for debugging).
  • Single Source of Truth: Avoids inconsistent state in multiple places.
  • Testable: Reducers are pure functions – easy to test.
  • Facilitates features like undo/redo or state persistence.

Cons:

  • Boilerplate: Need to define actions, action creators, reducers, and connect views. Can be verbose for small apps.
  • Overkill for simple UI: If app is small or state is local, Redux adds unnecessary complexity.
  • Performance: Large state trees and frequent updates can be slow; must optimize selectors and immutability.

Implementation Tips:

  • React Native: Use the official redux library. Typical pattern:
    // actions.js
    export const addItem = (item) => ({ type: "ADD_ITEM", payload: item });
    // reducer.js
    const initialState = { items: [] };
    function rootReducer(state = initialState, action) {
      switch (action.type) {
        case "ADD_ITEM":
          return { ...state, items: [...state.items, action.payload] };
        default:
          return state;
      }
    }
    
    Connect store to app via <Provider> and useSelector/useDispatch (Hooks) or connect().
  • Flutter: Use packages like flutter_redux. The Redux website explains: “Redux uses a one-way data flow”. You create a Store with a root reducer, dispatch actions, and use StoreConnector to rebuild widgets.
  • Consider lighter alternatives if Redux feels heavy: e.g. Context/ hooks in React, Provider/ChangeNotifier or BLoC in Flutter.
  • Use selector functions to pick only needed data from state to optimize renders.

Other Patterns
#

  • Clean Architecture (layered): A broad principle (from Uncle Bob) where code is divided into layers (UI, domain/use-cases, data). Components depend inward (UI knows domain, domain knows nothing of UI). Many architectures (VIPER, MVP) are implementations of this idea. Android’s official recommendations encourage a layered architecture with clear UI, domain, and data layers. For instance, Android docs: “Our recommended layered architecture favors separation of concerns… and follows unidirectional data flow”.
  • BLoC (Flutter): Business Logic Component – uses reactive streams (via StreamController) to manage state. It’s like MVVM with streams. Flutter’s bloc library encourages splitting UI from logic in blocs, which emit states. Use for complex state flows without manual setState.
  • Coordinator (iOS): A pattern for navigation: rather than pushing view controllers, a Coordinator object handles screen flow logic. This decouples view controllers from knowing about others. Not an official “architecture”, but common in large iOS apps.
  • Service/Repository Patterns: Common idea in many architectures – have Repository classes that abstract data sources (network, cache). Android Jetpack suggests repositories exposing data to UI. Not unique to mobile, but widely used (with DI).
  • MVU (Model-View-Update): Used in some cross-platform frameworks (Elm architecture, Redux is akin to MVU). Involves immutable state and update functions. Flutter’s GoRouter and some Xamarin frameworks hint at this.

Comparison Table of Patterns
#

PatternCategoryComplexityPerformance ImpactBest for
Bottom Navigation / Tab BarUILowLowApps with ≤5 top sections (social, media)
Navigation Drawer (Hamburger)UILowLowApps with many/secondary screens (settings, profile)
Segmented Control / Top TabsUILowLow2–4 related tabs within a section
Cards/List ViewsUIMediumMedium (scrolling lists)Content-driven apps (news, e-commerce)
Infinite ScrollUIMediumHigh (memory, network)Continuous feeds (social media, news)
Bottom Sheet / ModalUIMediumLowContextual actions (media controls, details)
MVCArchitectureLowLowSmall/simple apps, quick prototypes
MVPArchitectureMediumLowLegacy Android or highly testable code
MVVMArchitectureMediumLowModern Android (ViewModel), iOS (SwiftUI), cross-platform
VIPER / Clean ArchArchitectureHighLowLarge iOS apps needing modularity
Redux / FluxArchitectureHighMedium (global updates)Apps with complex shared state (React/Flutter)
BLoC / Reactive StreamsArchitectureMediumLowFlutter apps with event-driven state

Architecture Diagram (Component Relationships)
#

The following diagram shows a typical layered architecture (e.g. MVVM/Clean) where UI components depend on domain logic which depends on data services:

flowchart LR
    subgraph UI Layer
        View[View / ViewController]
        VM[ViewModel / Presenter]
    end
    subgraph Domain Layer
        Interactor[Interactor / UseCase]
    end
    subgraph Data Layer
        Repo[Repository / Service]
    end
    View --> VM --> Interactor --> Repo
    Repo --> Service[API / Database]

In this model:

  • View (or ViewController) displays UI and sends user events to ViewModel.
  • ViewModel/Presenter processes UI logic, prepares data for the view, and calls Interactor.
  • Interactor (UseCase) contains business logic and orchestrates data operations.
  • Repository/Service handles data access (network, cache, database). Each layer has a single responsibility, aiding testability and separation of concerns.

References
#

We cited official platform guidelines and authoritative sources for each pattern. Key references include Apple’s MVC documentation, Android’s Developer Guide on layered architecture, Flutter’s app architecture guide (MVVM), Smashing Magazine’s UX article on navigation patterns, and the objc.io VIPER article. Additional insights were drawn from Nielsen Norman Group on bottom sheets and the Redux documentation on unidirectional data flow.

Huy D.
Author
Huy D.
Mobile Solutions Architect with experience designing scalable iOS and Android applications in enterprise environments.