Skip to main content

Presenters and UI State

Reading time: ~15 minutes

Introduction​

The Presentation Layer is a critical component of Prism Architecture, responsible for managing the user interface and user interactions. At the heart of this layer are presenters, which handle UI logic and state management. This document explores the role of presenters in Prism Architecture, how they manage UI state, and how they communicate with other layers.

Presenters in Prism Architecture serve as the bridge between the user interface and the application's business logic. They transform domain data into presentation-ready formats, manage UI state, handle user interactions, and communicate with the Orchestration Layer. Understanding presenters and UI state management is essential for creating responsive, maintainable user interfaces that accurately reflect the application's domain model.

The Presentation Layer in Prism Architecture​

Purpose and Responsibilities​

The Presentation Layer in Prism Architecture is responsible for:

  1. Displaying Information: Presenting application data in a user-friendly format
  2. Capturing User Input: Receiving and validating user interactions
  3. Managing UI State: Tracking the state of UI components and screens
  4. Transforming Data: Converting domain data to presentation formats and vice versa
  5. Communicating with Business Logic: Translating user actions into business operations

The Presentation Layer encapsulates all UI concerns, allowing the rest of the application to focus on business logic without being tied to any specific UI framework or platform.

Key Components​

The Presentation Layer consists of several key components:

  1. UI Components: Visual elements that make up the user interface, from simple buttons to complex custom controls
  2. Presenters/ViewModels: Intermediaries between the UI and the application logic, handling presentation logic
  3. Screens: Complete user interfaces for specific functionality
  4. Navigation Controllers: Manage transitions between screens and the overall flow
  5. UI State Management: Handle storing and updating UI-specific state

These components work together to create a clean, maintainable separation between UI concerns and business logic.

PrismUI is the recommended presentation pattern for Prism Architecture, providing a structured approach to UI architecture that aligns with the core principles of the overall architecture.

Key Characteristics of PrismUI​

  1. Hierarchical Presenter Structure: Organizes presenters in a three-tier hierarchy that mirrors the UI component structure
  2. Intent-Based Communication: Uses explicit intent objects to represent user actions
  3. Unidirectional State Flow: Implements a predictable flow of state from presenters to views
  4. Clean Separation of Concerns: Separates UI logic from both view implementation and business logic

This structured approach creates a presentation layer that is both flexible and maintainable, with clear responsibilities and communication patterns.

Communication with Other Layers​

PrismUI communication follows the package delivery model used throughout Prism Architecture:

In this model:

  • Intent definitions reside in the Common Layer
  • State definitions reside in the Common Layer
  • Presenters create Intent objects using Common Layer definitions
  • Presenters process State objects using Common Layer definitions

The Presenter Hierarchy​

One of the key innovations in PrismUI is its hierarchical presenter structure, which organizes presenters into three distinct levels, each with specific responsibilities.

Top-Level Presenters​

Top-Level Presenters operate at the screen or flow level:

  • Screen Presenters: Manage full screens that are part of the main navigation flow
  • Modal Presenters: Manage full or partial screen overlays that temporarily interrupt the main flow
  • Top-Dialog Presenters: Manage complex dialogs with multiple components or independent state management

Key responsibilities:

  • Communicate directly with the Orchestration Layer
  • Manage navigation between screens or major sections
  • Coordinate across different features within a screen
  • Create and manage Mid-Level presenters
  • Process State objects received from the Orchestration Layer
  • Create Intent objects to send to the Orchestration Layer

Mid-Level Presenters​

Mid-Level Presenters manage logical segments or features within a screen:

  • Segment Presenters: Manage logical sections within Screens or Modals
  • Flow Presenters: Manage multi-step processes that might span multiple screens/views
  • Mid-Dialog Presenters: Manage simpler dialogs that present information or collect basic input

Key responsibilities:

  • Manage feature-specific state and logic
  • Create and manage Component-Level presenters
  • Forward significant intents to parent presenters
  • Transform screen-level state for their specific feature

Component-Level Presenters​

Component-Level Presenters manage individual UI components:

  • Component Presenters: Manage individual UI components
  • List Item Presenters: Manage items within lists or collections

Key responsibilities:

  • Handle component-specific UI logic
  • Maintain focused component state
  • Generate intents from user interactions
  • Receive tailored state from parent presenters

Presenter Hierarchy Flow​

This hierarchical flow ensures that:

  • Components only handle what they need
  • Intent and State objects flow through the architecture efficiently
  • Each layer has appropriate responsibilities
  • Communication maintains proper boundaries

View Types and Their Presenters​

Prism Architecture defines clear categories for different view types, each with specific presenter relationships:

Screens and Modals​

  • Screens: Full application screens in the main navigation flow
  • Modals: Full or partial screen overlays that interrupt flow
  • Top-Dialogs: Complex dialogs with multiple components
  • Presenter Level: Top-Level (direct access to Orchestration Layer)

Segments and Sections​

  • Segments: Logical sections of a screen containing multiple components
  • Flows: Multi-step process coordinators
  • Mid-Dialogs: Simpler dialogs managed by a parent presenter
  • Presenter Level: Mid-Level (communicates through parent presenter)

Individual Components​

  • Components: Individual UI elements
  • List Items: Items within collections
  • Popups and Toasts: Temporary UI elements
  • Presenter Level: Component-Level (communicates through parent presenter)

This structured approach ensures that each UI element has appropriate presenter support while maintaining a clean hierarchy of responsibilities.

UI State Management​

What is UI State?​

UI State represents the current condition of the user interface. It includes:

  1. Data State: The information being displayed
  2. Interaction State: The current state of user interactions
  3. Navigation State: Where the user is in the application
  4. Feedback State: Loading indicators, error messages, etc.
  5. Form State: The state of input fields and forms

UI State is distinct from domain state, focusing specifically on how information is presented rather than the underlying business data.

State Organization​

Just like Intent objects, State objects used in the Presentation Layer follow the package delivery approach:

State Structure​

In Prism Architecture, UI State is typically structured as immutable objects with a clear hierarchy:

  1. Screen State: The state for an entire screen, managed by Top-Level Presenters
  2. Feature State: State for specific features, managed by Mid-Level Presenters
  3. Component State: State for individual components, managed by Component Presenters

Each level contains only the state relevant to its scope, with higher levels passing down appropriate portions to lower levels.

State Flow Patterns​

UI State flows through the presenter hierarchy in a predictable manner:

  1. Downward Flow: State flows from higher-level to lower-level presenters
  2. Transformation: Each level transforms state for its specific needs
  3. Immutability: State objects are immutable, with new instances created for changes
  4. Selective Propagation: Only relevant state changes trigger updates

This unidirectional flow creates a predictable, maintainable state management system.

State Definition Location​

All State types used in the Presentation Layer should be defined in the Common Layer:

Common/
└── State/
├── Base/
│ └── BaseState.swift # Base interface for all states
├── UI/
│ ├── LoginScreenState.swift # Screen-level states
│ ├── ProfileFormState.swift # Feature-level states
│ └── ButtonState.swift # Component-level states
└── ...

This ensures that:

  • All layers can access the State definitions they need
  • State objects have consistent structure and behavior
  • State can flow naturally between layers
  • Communication maintains proper boundaries

Handling State Updates​

State updates in Prism Architecture follow these patterns:

  1. Event-Based Updates: State changes in response to events from the domain or user actions
  2. Atomic Updates: State is updated as a complete unit, not piecemeal
  3. Derived Values: Computed values based on the current state
  4. State Diffing: Only actual state changes trigger UI updates

These patterns ensure efficient, predictable state management across the application.

Intent-Based Communication​

What are Intents?​

Intents in Prism Architecture represent user actions or requests. They are:

  1. Explicit: Clearly defined objects with a specific purpose
  2. Immutable: Once created, they don't change
  3. Self-Contained: Include all data needed for the requested action
  4. Hierarchical: Organized to match the presenter hierarchy

Intents express what the user wants to accomplish, not how it should be implemented.

Intent Flow​

Intents flow upward through the presenter hierarchy:

  1. Generation: Component Presenters generate intents from user actions
  2. Bubbling: Intents bubble up to Mid-Level and Top-Level Presenters
  3. Handling: Each level either handles the intent or passes it upward
  4. Translation: Top-Level Presenters translate UI intents into Use Case requests

This upward flow ensures that intents are handled at the appropriate level of the architecture.

Intent Organization​

Just like State objects, all Intent types should be defined in the Common Layer:

Common/
└── Intent/
├── Base/
│ └── BaseIntent.swift # Base interface for all intents
├── UI/
│ ├── LoginIntent.swift # UI-specific intents
│ ├── NavigationIntent.swift # Navigation-related intents
│ └── FormIntent.swift # Form interaction intents
└── ...

Common Intent Patterns​

Typical intent patterns include:

  1. Action Intents: Request specific actions (e.g., SaveProfileIntent)
  2. Navigation Intents: Request navigation changes (e.g., ShowDetailsIntent)
  3. Data Intents: Request data changes (e.g., UpdateNameIntent)
  4. Event Intents: Notify about UI events (e.g., ItemSelectedIntent)

These patterns provide a structured approach to handling user interactions.

Intent Creation​

Intents are created in the Presentation Layer using definitions from the Common Layer:

Communication Between Presenter Levels​

Downward Data Flow​

Data flows down the presenter hierarchy with a targeted approach:

  1. Top-Level Presenters: Receive domain data from Orchestration Layer and transform it into screen-level state
  2. Mid-Level Presenters: Receive focused state from their parent and transform it for their specific feature
  3. Component-Level Presenters: Receive minimal, component-specific state from their parent

Each level only receives the state it needs, preventing unnecessary coupling and improving performance.

Upward Intent Flow​

Intents flow up the presenter hierarchy in a structured manner:

  1. Component-Level Presenters: Generate intents from user interactions
  2. Mid-Level Presenters: Handle intents they can process, bubble others up to their parent
  3. Top-Level Presenters: Handle screen-level intents and translate significant intents to Orchestration Layer requests

This pattern ensures that intents are handled at the appropriate level, with only significant intents reaching the Orchestration Layer.

Integration with Modern UI Frameworks​

Declarative UI Frameworks​

PrismUI works particularly well with declarative UI frameworks like SwiftUI, Jetpack Compose, and React:

  1. State-Driven Rendering: These frameworks naturally align with PrismUI's state-based approach
  2. Component Hierarchy: The component-based nature of these frameworks mirrors PrismUI's presenter hierarchy
  3. Unidirectional Data Flow: These frameworks support PrismUI's downward state flow
  4. Event Handling: They provide clean mechanisms for handling user interactions

This natural alignment makes PrismUI particularly effective with modern UI frameworks.

Framework-Specific Adaptations​

While the core principles remain consistent, PrismUI adapts to specific UI frameworks:

SwiftUI Integration​

Jetpack Compose Integration​

React Integration​

Testing Strategies​

Presenter Testing​

Presenters in PrismUI are designed for testability:

  1. Unit Testing: Test presenter logic in isolation
  2. Intent Testing: Verify correct handling of intents
  3. State Testing: Ensure proper state transformations
  4. Integration Testing: Test interaction with other components

These approaches enable comprehensive testing of presentation logic without requiring UI testing frameworks.

Component Testing​

For testing the complete UI with presenters:

  1. Snapshot Testing: Verify UI rendering for given states
  2. Interaction Testing: Test user interactions and resulting state changes
  3. Navigation Testing: Verify correct navigation flow based on intents
  4. Integration Testing: Test complete features with real or mock data

This multi-level testing approach ensures that both individual components and their interactions work correctly.

Common Anti-Patterns to Avoid​

UI Logic in Views​

Placing UI logic in views creates several problems:

  1. Reduced Testability: UI logic in views is harder to test
  2. Duplicated Logic: Similar logic may be repeated across views
  3. Tight Coupling: Views become tightly coupled to specific behaviors

Keep UI logic in presenters, not views.

Business Logic in Presenters​

Including business logic in presenters also creates issues:

  1. Layer Violation: Business logic belongs in the Domain or Orchestration Layer
  2. Reduced Reusability: Presenters become tied to specific business rules
  3. Testing Complexity: Testing becomes more complex

Keep business logic in the appropriate layers, not in presenters.

Bidirectional Data Binding​

While convenient, bidirectional data binding can cause problems:

  1. Unpredictable Updates: State changes can come from multiple sources
  2. Debugging Difficulty: It's harder to track the source of changes
  3. Cascade Effects: Changes can cascade unpredictably

Prefer unidirectional data flow with explicit updates.

Manual Intent and State Creation Outside Common Layer Definitions​

Creating Intent and State objects without using the Common Layer definitions causes inconsistency:

  1. Inconsistent Structure: Intent and State objects may have different structures
  2. Harder Maintenance: Changes to communication patterns require updates in multiple places
  3. Type Incompatibility: Objects may not be compatible with other layers' expectations

Always use the Common Layer definitions for Intent and State objects.

Real-World Example: Profile Feature​

To illustrate how these concepts work together in practice, consider a user profile feature:

Presenter Hierarchy​

  • Top-Level: ProfileScreenPresenter (manages overall profile screen)
  • Mid-Level: ProfileInfoPresenter (manages personal info section), ActivityFeedPresenter (manages activity section)
  • Component-Level: AvatarPresenter, NameFieldPresenter, ActivityItemPresenter

Intent and State Flow​

This structured approach creates a clear, traceable flow of both data and user actions through the system.

Common Layer's Role in Presentation​

The Common Layer plays a crucial role in the Presentation Layer by providing:

  1. Intent Definitions: Base types and specific intent types used by presenters
  2. State Definitions: Base types and specific state types used by presenters
  3. Display Objects: UI-ready data structures for presentation
  4. Formatting Utilities: Helpers for formatting data for display
  5. Validation Utilities: Tools for validating user input

By defining all these types in the Common Layer, we ensure consistency across the application while maintaining proper layer boundaries.

Common Layer Structure for Presentation Types​

Common/
├── Intent/
│ ├── Base/
│ │ └── BaseIntent.swift # Base intent interface
│ └── UI/
│ ├── ProfileIntent.swift # Profile screen intents
│ ├── NavigationIntent.swift # Navigation intents
│ └── FormIntent.swift # Form interaction intents
├── State/
│ ├── Base/
│ │ └── BaseState.swift # Base state interface
│ └── UI/
│ ├── ProfileScreenState.swift # Profile screen state
│ ├── ProfileFormState.swift # Profile form state
│ └── NavbarState.swift # Navigation state
└── DisplayObjects/
├── UserDisplayObject.swift # User presentation data
├── ActivityItemDO.swift # Activity item for display
└── ...

Conclusion​

Presenters and UI state management are critical components of Prism Architecture's Presentation Layer. The hierarchical presenter structure, with Top-Level, Mid-Level, and Component-Level Presenters, provides a clear organization that mirrors the natural structure of user interfaces. The unidirectional flow of state down and intents up creates a predictable, maintainable system.

By using Intent and State definitions from the Common Layer, the Presentation Layer maintains consistent communication patterns while preserving proper architectural boundaries. This approach aligns with the overall package delivery model used throughout Prism Architecture.

When implementing presenters and UI state management, focus on maintaining the hierarchical structure, keeping state immutable and flowing downward, using explicit intents for user actions, and transforming data appropriately between domain and presentation formats. By following these principles, you can create user interfaces that are both responsive to users and well-integrated with your application's business logic.