Youll
Modules

Explore

Content discovery screen where users browse by categories, subcategories, facilitators, and tags. Supports two operational modes, generic tag-based browsing and a custom home-like feed.

The Explore module is the content discovery screen where users browse the full content catalog. Unlike the Home Screen, which shows a personalized feed of recommended content, Explore provides structured browsing through categories, subcategories, facilitators, and tags.

Explore has two operational modes controlled by feature flags:

ModeViewModelData SourceLayout
Generic (default)ExploreFeedViewModelGenericExploreService (GraphQL)Tag-filtered content grid
CustomHomeViewModel (reused)ExploreService (GraphQL, cached)Home-like sectioned feed

Like all core content modules, Explore's screens live in the Core package and its coordinator lives in the Client package. For the underlying patterns, see:

Activation

Explore is activated by including .explore in the customer app's tab selection:

func makeTabItems() -> [TabItem] {
    [.showcase, .explore, .library, .journeys, .profile]
}

The view type is determined automatically at coordinator creation time based on feature flags:

private func createExploreCoordinator() -> Coordinator {
    var exploreType: ExploreViewType = .generic
    if !config.flagManager.isActive(flag: .isLightVersion) &&
       config.flagManager.isActive(flag: .exploreEnabled) {
        exploreType = .custom
    }

    let exploreCoordinator = ExploreCoordinator(
        config: config,
        timerModuleInterface: timerModuleInterface,
        type: exploreType)
    exploreCoordinator.start()
    addChildCoordinator(exploreCoordinator, flow: .explore)
    return exploreCoordinator
}
Flag CombinationResult
exploreEnabled active AND NOT isLightVersionCustom mode (home-like feed)
isLightVersion activeGeneric mode (tag-based)
exploreEnabled inactiveGeneric mode (tag-based)

Some customer apps use .exploreShowcase instead of .explore. This tab item uses ShowcaseCoordinator (the Home v2 coordinator) with isUsedForExplore: true, providing a Showcase-style layout for the Explore tab. This is a third alternative, separate from the generic/custom modes described here.

Architecture

Explore spans three packages in the Bricks dependency graph:

YoullNetwork
  └── GenericExploreService / ExploreService    ← GraphQL queries

Core   │
  ├── ExploreFeedViewController                 ← UIViewController hosting SwiftUI
  ├── ExploreFeedViewModel (228 lines)          ← Data + navigation delegates
  ├── ExploreFeedDataProvider (192 lines)        ← Pagination + state machine
  ├── ContentItem                               ← Shared content model
  ├── Tag / ExploreTag                          ← Tag models
  ├── CategoryViewController + ViewModel + View  ← Category details
  ├── SubcategoryViewController + ViewModel + View ← Subcategory details
  └── Facilitator screens                        ← Facilitator details + content list

Client │
  └── ExploreCoordinator (218 lines)            ← Navigation + mode selection

ExploreCoordinator

ExploreCoordinator is 218 lines, significantly simpler than HomeCoordinator's 1,146 lines. It manages the Explore tab's navigation and handles the dual view type logic.

The coordinator receives one optional module interface:

  • TimerModuleInterface: if injected, the Explore screen can show a timer button and navigate to timer screens

On start(), the coordinator branches based on ExploreViewType:

enum ExploreViewType {
    case generic    // Tag-based content grid
    case custom     // Home-like sectioned feed
}
  • Generic: calls createExploreFeedController() which creates ExploreFeedViewController with ExploreFeedViewModel
  • Custom: calls createHomeController() which creates HomeViewController with HomeViewModel configured as isUsedForExploreFeed: true

ExploreFeedViewController

ExploreFeedViewController (169 lines) is a UIViewController that hosts a SwiftUI view (ExploreFeedView). It uses ExploreBaseView as its root view, which provides the navigation bar, and embeds the SwiftUI content grid below.

ExploreFeedViewModel

ExploreFeedViewModel (228 lines) manages the generic Explore feed. It holds:

  • ExploreFeedDataProvider for data loading and pagination
  • TagsBarDataProvider for tag selection state
  • screenConfig: ExploreFeedScreenConfig for visual configuration

The ViewModel communicates user actions to the coordinator through the ExploreFeedDelegate protocol.

View Types

Generic Mode

The default mode shows a tag-filtered content grid.

Tags bar. A horizontal scrollable bar at the top displays available tags. When the user selects a tag, the content grid filters to show only items matching that tag. The .all tag shows all categories. Tags are loaded via ExploreTagsAPI.getExploreTags() and cached in Settings.exploreTags (UserDefaults) for offline display.

Content grid. Below the tags bar, a grid displays ContentItem objects. Each item can be a category, subcategory, or session. Items show a thumbnail, title, lock status (for subscription-gated content), and viewed percentage.

Pagination. The data provider uses offset-based pagination. When the user scrolls near the bottom, ExploreFeedDataProvider requests the next page. New items are deduplicated against existing items to prevent duplicates during rapid scrolling.

State machine. The data provider follows a state pattern:

StateMeaning
.idleReady, no load in progress
.loadingInitial load in progress
.reloadingPull-to-refresh in progress
.failure(Error)Load failed, showing error state

Custom Mode

When exploreEnabled is active and isLightVersion is not, Explore uses the same HomeViewModel that powers the Home Screen (v1). The coordinator creates a HomeViewController with isUsedForExploreFeed: true, which tells the ViewModel to fetch data via ExploreService instead of HomeFeedService.

The custom mode provides a home-like sectioned feed with featured content, tagged collections, and promotional sections. Data is fetched via FeedAPI.getExploreFeedSections() and cached in Core Data through DataApiCache.

For details on the feed section types, data flow, and configuration available in custom mode, see the Home Screen documentation.

Screens

Explore Feed

ComponentFileLinesPurpose
ExploreFeedViewControllerFeed/ExploreFeedViewController.swift169UIViewController hosting SwiftUI view
ExploreFeedViewModelFeed/ExploreFeedViewModel.swift228Data management, tag selection, navigation delegates
ExploreFeedDataProviderFeed/ExploreFeedDataProvider.swift192Pagination, deduplication, state machine
ExploreFeedAPIFeed/ExploreFeedAPI.swift80API protocol definitions (ExploreContentAPI + ExploreTagsAPI)
ExploreFeedViewFeed/View/ExploreFeedView.swiftSwiftUI content grid
ExploreBaseViewFeed/ExploreBaseView.swift31Base UIView with navigation bar
TagsBarDataProviderFeed/Tags Bar/TagsBarDataProvider.swiftTag selection state management
ContentItemFeed/Item/ContentItem.swiftShared content model (category, subcategory, session)
ItemCellControllerFeed/Item/ItemCellController.swiftGrid layout and cell configuration

Category Details

When a user taps a category in the Explore feed, the coordinator pushes the category details screen. This screen shows a grid of subcategories within that category.

ComponentFileLinesPurpose
CategoryViewControllerCategory Details/CategoryViewController.swiftCategory screen container
CategoryViewModelCategory Details/CategoryViewModel.swift60Category state management
CategoryViewCategory Details/CategoryView.swift150SwiftUI subcategory grid
CategoryDetailsDataProviderCategory Details/CategoryDetailsDataProvider.swiftLoads subcategories
CategoryDetailsScreenConfigCategory Details/CategoryDetailsScreenConfig.swiftVisual configuration

Subcategory Details

Tapping a subcategory shows its sessions. This screen also displays user reviews with star ratings.

ComponentFileLinesPurpose
SubcategoryViewControllerSubcategory Details/SubcategoryViewController.swift160Subcategory screen container
SubcategoryViewModelSubcategory Details/SubcategoryViewModel.swift130Session loading and state
SubcategoryViewSubcategory Details/SubcategoryView.swift220SwiftUI session grid with reviews
SubcategoryDetailsDataProviderSubcategory Details/SubcategoryDetailsDataProvider.swift120Loads sessions and reviews
ReviewsWidgetConfigSubcategory Details/ReviewsWidgetConfig.swiftStar rating widget configuration

Facilitators

Facilitator screens show instructor/teacher profiles and their content. There are two screens: a details view and a content list.

ComponentFilePurpose
FacilitatorDetailsViewControllerFacilitators/FacilitatorDetails/Facilitator profile display
FacilitatorDetailsViewModelFacilitators/FacilitatorDetails/Profile data management
FacilitatorDetailsViewFacilitators/FacilitatorDetails/SwiftUI profile view
FacilitatorContentsListViewControllerFacilitators/FacilitatorContentsList/List of facilitator's content
FacilitatorContentsListViewModelFacilitators/FacilitatorContentsList/Content list data
FeedFacilitatorItemFacilitators/Data/Model: id, fullName, profileImage, coverImage, content, order

Data Flow

Generic Mode

User opens Explore tab


ExploreFeedDataProvider.loadData()

    ├──▸ TagsBarDataProvider loads tags
    │    ├── ExploreTagsAPI.getExploreTags() (GraphQL)
    │    └── Cache tags in Settings.exploreTags

    ├──▸ If tag == .all:
    │    └── ExploreContentAPI.getAllCategories() → [BNCategory]

    ├──▸ If tag == .tag(Tag):
    │    └── ExploreContentAPI.getContent(with: tag, offset:) → [ContentItem]

    └──▸ Emit state update → ExploreFeedView renders grid

Pagination adds more items when the user scrolls:

User scrolls near bottom


ExploreFeedDataProvider.loadMore()

    ├── Increment offset
    ├── ExploreContentAPI.getContent(with: tag, offset:)
    ├── Deduplicate against existing items
    └── Append to content list

Custom Mode

User opens Explore tab (custom mode)


HomeFeedDataProvider.loadData()

    ├──▸ ExploreService.getExploreFeedSections() (GraphQL)
    │    └── FeedAPI.getExploreFeedSections()

    ├──▸ Cache in Core Data via DataApiCache

    └──▸ Return sectioned feed (same structure as Home v1)

Data Models

Tag:

@frozen public struct Tag: Codable, Hashable, Sendable {
    public let id: String
    public let name: String
    public let imageUrl: URL?
    public let sortBy: SliceSortBy?
    public let sortDirection: SliceSortDirection?
}

public enum ExploreTag: Hashable, Equatable {
    case all
    case tag(Tag)
}

ContentItem:

public struct ContentItem: Hashable, Sendable {
    public enum ContentType: String, Sendable {
        case category, subcategory, session
    }

    // Key properties: id, title, thumbnail, contentType,
    // lock status, viewed percentage, content metadata
}

ContentItem is the shared model used across all Explore screens (feed grid, category details, subcategory details).

ExploreFeedAPI protocols:

public typealias ExploreFeedAPI = ExploreContentAPI & ExploreTagsAPI

public protocol ExploreContentAPI {
    var pageSize: Int { get }
    func getAllCategories() -> AnyPublisher<[BNCategory], Error>
    func getContent(with tag: Tag, offset: Int, sortInfo: ...) -> AnyPublisher<[ContentItem], Error>
    func getSubcategories(forCategory categoryId: String, ...) -> AnyPublisher<[ContentItem], Error>
}

public protocol ExploreTagsAPI {
    func getExploreTags() -> AnyPublisher<[Tag], Error>
}

Configuration

ExploreScreenConfigFactoryType

Customer apps implement this protocol to configure the Explore screen's visual appearance:

protocol ExploreScreenConfigFactoryType {
    func makeExploreFeedScreenConfig(bgUrl: URL?) -> ExploreFeedScreenConfig
}

ExploreFeedScreenConfig

The ExploreFeedScreenConfig struct controls the visual appearance of the generic Explore feed:

AreaWhat It Controls
Status barLight/dark style
Navigation barTitle text, logo image, search button image
Tags barBackground color, shadow, spacing, "All" pill toggle, tag cell theme
Content gridInsets, element spacing, cell theme configs, content type themes
BackgroundBackground image URL, tint color
SkeletonLoading state placeholder appearance
Error stateError screen appearance and retry button

Other Screen Configs

Each detail screen has its own configuration struct:

ConfigScreenKey Controls
CategoryDetailsScreenConfigCategory detailsCategory grid layout and theming
SubcategoryDetailsScreenConfigSubcategory detailsSession grid, header config
SubcategoryDetailsHeaderConfigSubcategory headerHeader image, title styling
FacilitatorDetailsScreenConfigFacilitator profileProfile layout, cover image
FacilitatorContentsListScreenConfigFacilitator contentContent list layout
ReviewsWidgetConfigSubcategory reviewsStar rating display

ExploreFeedDelegate

The ExploreFeedDelegate protocol bridges ExploreFeedViewModel (in Core) with ExploreCoordinator (in Client):

Delegate MethodNavigation Target
showDetails(forSession:autoPlayContentIDs:)Content playback via ContentDetailsCoordinator.shared
showDetails(forCategory:)Category details screen (push)
showDetails(forSubcategory:)Subcategory details screen (push)
showDetails(forSubcategoryId:)Subcategory details by ID (push)

Additional navigation from the Explore screen:

ActionTarget
Search button tapSearchCoordinator.shared.createSearchController()
Timer button taptimerModuleInterface?.showTimerSettingsController()
Tag selectiondisplayTaggedItemsFeed() (push tag feed screen)

Explore supports deep linking for content-related destinations:

Deep Link CaseBehavior
explore(tagId:)Opens Explore with a pre-selected tag via preselectTagInExploreScreen(tagId:)
category(id:)Pushes category details screen
subcategory(id:)Pushes subcategory details screen
tag(id:, sortInfo:)Pushes tag feed screen with sort parameters

Cross-Module Integration

ModuleIntegration Point
PlayerSessions open via ContentDetailsCoordinator.shared with auto-play queue
SearchSearch button navigates to SearchCoordinator.shared
TimerTimer button available if TimerModuleInterface is injected
Home ScreenCustom mode reuses HomeViewModel with isUsedForExploreFeed: true

Explore does not directly integrate with Tuya, Biometrics, or Community modules.

For Agents

Reading Order

When working with the Explore module, read files in this order:

  1. Client/Coordinators/Tabs/ExploreCoordinator.swift (218 lines) for the dual mode logic, navigation, and delegate handling
  2. Core/Screens/Explore/Feed/ExploreFeedViewModel.swift (228 lines) for the ViewModel, ExploreFeedDelegate protocol, and data provider usage
  3. Core/Screens/Explore/Feed/ExploreFeedDataProvider.swift (192 lines) for pagination, deduplication, and state machine
  4. Core/Screens/Explore/Feed/ExploreFeedAPI.swift (80 lines) for the API protocol definitions
  5. Core/Screens/Explore/Feed/ExploreFeedScreenConfig.swift (195 lines) for the configuration structure
  6. Client/App/Screen Config/FactoryTypes.swift (search for ExploreScreenConfigFactoryType) for the configuration protocol

Key Files

ComponentFile Path
ExploreCoordinatorBricks/modules/Client/Client/Coordinators/Tabs/ExploreCoordinator.swift
ExploreFeedViewControllerBricks/modules/Core/Core/Screens/Explore/Feed/ExploreFeedViewController.swift
ExploreFeedViewModelBricks/modules/Core/Core/Screens/Explore/Feed/ExploreFeedViewModel.swift
ExploreFeedDataProviderBricks/modules/Core/Core/Screens/Explore/Feed/ExploreFeedDataProvider.swift
ExploreFeedAPIBricks/modules/Core/Core/Screens/Explore/Feed/ExploreFeedAPI.swift
ExploreFeedScreenConfigBricks/modules/Core/Core/Screens/Explore/Feed/ExploreFeedScreenConfig.swift
ExploreFeedView (SwiftUI)Bricks/modules/Core/Core/Screens/Explore/Feed/View/ExploreFeedView.swift
TagsBarDataProviderBricks/modules/Core/Core/Screens/Explore/Feed/Tags Bar/TagsBarDataProvider.swift
ContentItemBricks/modules/Core/Core/Screens/Explore/Feed/Item/ContentItem.swift
CategoryViewControllerBricks/modules/Core/Core/Screens/Explore/Category Details/CategoryViewController.swift
SubcategoryViewControllerBricks/modules/Core/Core/Screens/Explore/Subcategory Details/SubcategoryViewController.swift
FacilitatorDetailsViewControllerBricks/modules/Core/Core/Screens/Explore/Facilitators/FacilitatorDetails/FacilitatorDetailsViewController.swift
FacilitatorContentsListBricks/modules/Core/Core/Screens/Explore/Facilitators/FacilitatorContentsList/FacilitatorContentsListViewController.swift
ExploreScreenConfigFactoryTypeBricks/modules/Client/Client/App/Screen Config/FactoryTypes.swift
Tab setupBricks/modules/Client/Client/Coordinators/Main/MainCoordinators+TabSetup.swift

Tips

  • To understand the two modes: check ExploreCoordinator.start() which branches on ExploreViewType. Generic creates ExploreFeedViewController, custom creates HomeViewController with isUsedForExploreFeed: true.
  • Tags are cached in Settings.exploreTags (UserDefaults) so they display immediately on next launch while fresh tags load from the server.
  • ContentItem is shared across all Explore screens. It uses a ContentType enum (.category, .subcategory, .session) to differentiate item types.
  • ExploreCoordinator is only 218 lines, much simpler than HomeCoordinator's 1,146 lines. Most navigation delegates to ContentDetailsCoordinator.shared.
  • Custom mode is essentially Home v1 in a different tab. If you understand the Home feed, you understand custom Explore. The only difference is data source (ExploreService vs HomeFeedService).
  • To add a new content type to the grid: update ContentItem.ContentType, add handling in ItemCellController, and update ExploreFeedDelegate if navigation differs.

What's Next

On this page