<!-- AI agents: this is the Markdown version of an AppNorth documentation page. Index: https://www.appnorth.app/llms.txt -->

# UIKit integration

Host AppNorth's SwiftUI views in `UIHostingController`. Keep your existing app delegate, navigation, and dependency injection; configure and retain the client once per session.

## Present community {#community}

The community view already supplies a navigation stack. Present its hosting controller directly rather than adding another navigation container.

```swift title="CommunityPresentation.swift"
import UIKit
import SwiftUI
import AppNorth

@MainActor
func presentCommunity(from presenter: UIViewController, client: AppNorthClient) {
    let controller = UIHostingController(rootView: AppNorthView(client: client))
    controller.modalPresentationStyle = .pageSheet
    presenter.present(controller, animated: true)
}
```

The default sheet can be dismissed by swiping. If your app requires an explicit close button or a full-screen presentation, add a host-owned SwiftUI wrapper with a dismissal control.

## Present support with a close button {#chat}

The chat view is conversation content, so you can put it inside your UIKit navigation controller:

```swift title="SupportViewController.swift"
import UIKit
import SwiftUI
import AppNorth

@MainActor
final class SupportViewController: UIHostingController<AppNorthChatView> {
    init(client: AppNorthClient) {
        super.init(rootView: AppNorthChatView(client: client))
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) { fatalError("Use init(client:)") }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Support"
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .done,
            target: self,
            action: #selector(closeSupport)
        )
    }

    @objc private func closeSupport() {
        dismiss(animated: true)
    }
}

@MainActor
func presentSupport(from presenter: UIViewController, client: AppNorthClient) {
    let navigation = UINavigationController(
        rootViewController: SupportViewController(client: client)
    )
    presenter.present(navigation, animated: true)
}
```

The built-in chat view manages visible/foreground polling. If you use `AppNorthChatModel` directly, you own its start/stop lifecycle. For an embedded hosting controller, use UIKit's normal `addChild`, view constraints, and `didMove(toParent:)` containment lifecycle.

## Keep identity and appearance aligned {#lifecycle}

Install the signed credential before presenting identified screens. Dismiss them before logout or account changes, clear the credential and account state, and create new controllers for the next session.

Pass `copy`, `theme`, and `paywalls` to the underlying SwiftUI view when needed. In a multiwindow app, supply the active window's presenter to the [RevenueCat helper](/docs/paywall-cards#helper) so a card opens above the correct chat.

## Verify {#verify}

1. Present and dismiss both screens from the existing UIKit app.
2. Open the keyboard, rotate the device, and test large text without clipping the composer.
3. Background and foreground support, then dismiss it; verify polling stops when hidden.
4. Switch accounts and confirm the newly presented controller has no previous account content.
