Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Using SwiftUI with UIKit

At a glance

Item Summary
Purpose Embed SwiftUI views in UIKit controllers and cells, including two-way observable data flow.
App architecture MainMenuViewController routes to three independent examples: child UIHostingController containment, cell-level UIHostingConfiguration, and a diffable UIKit list whose private observable models bind into editable SwiftUI cell content.
Main patterns Framework adapter bridges, Example factory router, Observable model data flow
Project style Ten Swift files; a UIKit navigation shell with three focused SwiftUI integration examples and four reusable SwiftUI cell views.

Project structure

Source bundle/
├── UseSwiftUIWithUIKit/
│   ├── AppDelegate.swift
│   ├── Examples/
│   │   ├── SwiftUI in Cells/
│   │   │   ├── SwiftUI Views/
│   │   │   │   ├── HeartRateCellView.swift
│   │   │   │   ├── SleepCellView.swift
│   │   │   │   ├── HealthCategoryCellView.swift
│   │   │   │   └── StepCountCellView.swift
│   │   │   └── HostingConfigurationExample.swift
│   │   ├── Data Flow with SwiftUI Cells/
│   │   │   └── CellDataFlowExample.swift
│   │   └── SwiftUI View Controllers/
│   │       └── HostingControllerExample.swift
│   ├── MainMenuViewController.swift
│   └── SceneDelegate.swift
├── Configuration/
│   └── SampleCode.xcconfig
└── UseSwiftUIWithUIKit.xcodeproj/
    └── .xcodesamplecode.plist

Structure observations

  • The menu is a small factory router; each example is its own composition root rather than one cross-example architecture.
  • Controller embedding follows UIKit child-containment rules, while cell embedding uses content configuration with no child controller.
  • The data-flow example separates collection identity updates from property updates on each ObservableObject.

Overall architecture

Reference code

UseSwiftUIWithUIKit/MainMenuViewController.swift:11 — example factory registry

private struct MenuItem {
    var title: String
    var subtitle: String
    var viewControllerProvider: () -> UIViewController

    static let allExamples = [
        MenuItem(title: "SwiftUI View Controllers",
                 subtitle: "Using UIHostingController",
                 viewControllerProvider: { HostingControllerViewController() }),
        MenuItem(title: "SwiftUI in Cells",
                 subtitle: "Using UIHostingConfiguration",
                 viewControllerProvider: { HostingConfigurationViewController() })
    ]
}

Interpretation

The sample compares integration boundaries, not competing app architectures. A hosting controller adapts SwiftUI into the UIKit controller hierarchy; a hosting configuration adapts SwiftUI into cell content; observable objects allow the two frameworks to share state while the UIKit data store retains collection ownership.

Ownership and state

Ownership evidence

UseSwiftUIWithUIKit/Examples/Data Flow with SwiftUI Cells/CellDataFlowExample.swift:140 — UIKit list and store ownership

class MedicalConditionsViewController: UIViewController {
    private var dataStore = DataStore()
    private var collectionView: UICollectionView!
    private var dataSource:
        UICollectionViewDiffableDataSource<Section, MedicalCondition.ID>!

    override func loadView() {
        setUpCollectionView()
        view = collectionView
    }
}
Owner Object or state Relationship Mutation authority
MainMenuViewController Menu collection view Creates and privately retains the UIKit list It invokes the selected item’s controller factory and pushes the result.
HostingControllerViewController Alert models and hosting-controller array Creates private observable models and child controllers It performs containment and updates surrounding UIKit summary UI.
HostingConfigurationViewController Static example data and cell registrations Privately retains collection/cell configuration state It chooses which SwiftUI view configures each cell type.
MedicalConditionsViewController Data store, collection view, diffable source Creates and privately retains all list collaborators It mutates collection membership and applies stable-ID snapshots.
Private DataStore Ordered/dictionary medical-condition collection Owns both indexes It alone inserts, deletes, sorts, and shuffles the collection.
MedicalConditionView Observed condition reference Non-owning semantic observation of a store-owned model The binding writes the model’s published text.

Composition denotes source-visible construction and retained value state. Aggregation denotes a stored or injected collaborator whose exclusive lifetime the source does not prove.

Class and protocol design

UseSwiftUIWithUIKit/Examples/SwiftUI View Controllers/HostingControllerExample.swift:136 — SwiftUI child-controller adapter

    private func createHostingControllers() {
        for alert in alerts {
            let alertView = HeartHealthAlertView(alert: alert)
            let hostingController = UIHostingController(rootView: alertView)
            hostingController.sizingOptions = .intrinsicContentSize
            hostingControllers.append(hostingController)
        }
    }

    private func setUpViews() {
        hostingControllers.forEach { addChild($0) }
        // Add each hosted view to the UIKit hierarchy.
        hostingControllers.forEach { $0.didMove(toParent: self) }
    }
Type Responsibility Depends on or conforms to
MainMenuViewController / MenuItem Display and instantiate independent examples UIKit collection callbacks plus closure factories
HostingControllerViewController Owns SwiftUI child-controller containment UIViewController, UIHostingController
HostingConfigurationViewController Builds cells whose content is SwiftUI UIViewController, UIHostingConfiguration
MedicalConditionsViewController Coordinates UIKit collection membership with SwiftUI-bound cell models Diffable UIKit list and hosting configurations
HeartHealthAlert / MedicalCondition Publish mutable state observed by SwiftUI ObservableObject; MedicalCondition also Identifiable
SwiftUI cell/view structs Render framework-neutral value or observable model state View

View, ObservableObject, Identifiable, and UIKit data-source/delegate roles are framework contracts. The sample defines no local protocol abstraction; its reusable boundaries are adapters, observable models, and factory closures.

Access control

Symbol Access Verified effect Design reason
MenuItem top-level private Visible only within MainMenuViewController.swift The route registry is an implementation detail of the menu file.
HeartHealthAlert, SwiftUI alert view top-level private Confined to the hosting-controller example file Prevents one demonstration’s teaching model from becoming a cross-example API.
MedicalCondition, DataStore, Section, MedicalConditionView top-level private Confined to the data-flow example file The file is the feature boundary; only its UIKit entry controller must be routable.
Entry view-controller types implicit internal The menu can construct them across files They are app routes, not external library symbols.
@MainActor model annotations actor isolation, not access control Serialize observable UI model access on the main actor UIKit/SwiftUI state mutation shares one UI concurrency domain.

Reference code

UseSwiftUIWithUIKit/Examples/Data Flow with SwiftUI Cells/CellDataFlowExample.swift:19 — file-private actor-isolated model

@MainActor
private class MedicalCondition: Identifiable, ObservableObject {
    let id: UUID
    @Published var text: String
}

@MainActor
private class DataStore {
    private var data: (
        conditions: [MedicalCondition],
        conditionsByID: [MedicalCondition.ID: MedicalCondition]) = ([], [:])
}

Here, top-level private makes each example file a hard implementation boundary. @MainActor adds concurrency isolation but does not widen or narrow visibility. The sample exposes no public, open, or fileprivate surface.

Logic ownership and placement

Logic Owning type or file Why it lives there
Example registration and navigation MainMenuViewController / private MenuItem The menu knows available examples and constructs their public-in-module entry controllers.
Child-controller containment HostingControllerViewController UIKit parent/child lifecycle belongs to the containing controller.
Cell SwiftUI composition Cell registrations in hosting examples The registration owns the UIKit-to-SwiftUI content adapter.
Collection membership and stable identity Private data-flow DataStore and UIKit controller Store changes produce diffable snapshots keyed by model IDs.
Per-item text mutation MedicalCondition plus MedicalConditionView binding Observable object changes refresh only interested SwiftUI content.

Design patterns

Pattern Source evidence Purpose or tradeoff
Framework adapter bridges UseSwiftUIWithUIKit/Examples/SwiftUI View Controllers/HostingControllerExample.swift:140 Adapts SwiftUI content to controller containment; hosting configurations adapt it to cells.
Example factory router UseSwiftUIWithUIKit/MainMenuViewController.swift:14 Registers independent demonstrations without a switch over controller types.
Observable model data flow UseSwiftUIWithUIKit/Examples/Data Flow with SwiftUI Cells/CellDataFlowExample.swift:20 Shares per-item mutable state across UIKit-owned lists and SwiftUI views.

Naming conventions

  • Example entry controllers name the bridge (HostingController, HostingConfiguration, MedicalConditions).
  • Private models use domain names (HeartHealthAlert, MedicalCondition, DataStore) rather than framework mechanics.
  • Adapter/view names state content (HeartRateCellView, MedicalConditionView).
  • One-file examples intentionally colocate private models, SwiftUI views, and their UIKit composition root.

Architecture takeaways

  • Choose UIHostingController for controller containment and UIHostingConfiguration for cell content.
  • Keep UIKit ownership of collection identity separate from observable mutation of one item’s properties.
  • Use file-level private types to stop teaching-example details from becoming accidental module architecture.

Source map

Source file Architectural role
UseSwiftUIWithUIKit/MainMenuViewController.swift Private example registry and navigation
UseSwiftUIWithUIKit/Examples/SwiftUI View Controllers/HostingControllerExample.swift Observable alerts and hosting-controller containment
UseSwiftUIWithUIKit/Examples/SwiftUI in Cells/HostingConfigurationExample.swift Value-driven SwiftUI cell configurations
UseSwiftUIWithUIKit/Examples/Data Flow with SwiftUI Cells/CellDataFlowExample.swift Observable data flow, private store, IDs, and hosted editable cells
UseSwiftUIWithUIKit/Examples/SwiftUI in Cells/SwiftUI Views/HealthCategoryCellView.swift Representative reusable SwiftUI cell view
UseSwiftUIWithUIKit/AppDelegate.swift / UseSwiftUIWithUIKit/SceneDelegate.swift Application and scene lifecycle only