---
title: Dynamic Navigation in Apple
description: Learn how to enable dynamic rendering of new stories by setting up catch-all navigation in Apple.
url: https://www.storyblok.com/docs/guides/apple/dynamic-routing
---

# Dynamic Navigation in Apple

Use [NavigationStack](https://developer.apple.com/documentation/swiftui/navigationstack) to set up a catch-all navigation strategy in your Apple project and render new stories dynamically.

## Create a story view

Create a `StoryView.swift` file to define a reusable view that can display any story, fetched by either its slug or UUID.

Example/StoryView.swift

```swift
import SwiftUI
import Combine

import StoryblokClient

struct StoryView<Content: Decodable & View>: View {

    private let publisher: AnyPublisher<Story<Content>, Never>

    @State private var story: Story<Content>?

    init(
        _ publisher: AnyPublisher<Story<Content>, StoryblokClient<Block>.Error>,
        story: Story<Content>? = nil
    ) {
        self.publisher = publisher
            .receive(on: DispatchQueue.main)
            .catch { error in Just(story).compactMap { $0 } }
            .eraseToAnyPublisher()
        self._story = State(initialValue: story)
    }

    var body: some View {
        ScrollView {
            if let story {
                story.content
            } else {
                ProgressView()
                    .progressViewStyle(.circular)
                    .frame(maxWidth: .infinity, minHeight: 200)
            }
        }
        .onReceive(publisher) { value in story = value }
        .refreshable {
            for await value in publisher.values {
                story = value
            }
        }
    }
}
```

`StoryView` moves the story fetching and pull-to-refresh implementation into a reusable view that can display any story publisher. It can be initialized with a [`Story`](https://storyblok.github.io/storyblok-swift/documentation/storyblokclient/story) instance for immediate display when navigating between screens.

## Set up the navigation stack

In your `ExampleApp.swift` file, replace the inline fetching implementation with a [`NavigationStack`](https://developer.apple.com/documentation/swiftui/navigationstack) rendering a `StoryView`, with a navigation destination for each way a story can be identified.

Example/ExampleApp.swift

```swift
import SwiftUI
import Combine
import StoryblokClient
internal import URLSessionExtension

@main
struct ExampleApp: App {
    @State private var path = NavigationPath()

    private let client = StoryblokClient(
        library: Block.self,
        accessToken: "YOUR_ACCESS_TOKEN",
        version: {
            #if DEBUG
            .draft
            #else
            .published
            #endif
        }(),
        region: .eu // Choose the correct region from your Space.
    )

    private let publisher: AnyPublisher<Story<Block>, Never>

    init() {
        publisher = client.story("home")
            .receive(on: DispatchQueue.main)
            .catch { error in Empty() }
            .eraseToAnyPublisher()
    }

    @State private var story: Story<Block>?

    var body: some Scene {
        WindowGroup {
            ScrollView {
                if let story {
                    story.content
                } else {
                    ProgressView()
                        .progressViewStyle(.circular)
                        .frame(maxWidth: .infinity, minHeight: 200)
                }
            }
            .onReceive(publisher) { value in story = value }
            .refreshable {
                for await value in publisher.values {
                    story = value
                }
            }
            NavigationStack(path: $path) {
                StoryView<Block>(client.story("home"))
                    .navigationDestination(for: String.self) { slug in
                        StoryView<Block>(client.story(slug))
                    }
                    .navigationDestination(for: UUID.self) { uuid in
                        StoryView<Block>(client.story(uuid))
                    }
                    .navigationDestination(for: Story<Block>.self) { story in
                        StoryView(client.story(story.uuid), story: story)
                    }
            }
        }
    }
}
```

The three [`navigationDestination`](https://developer.apple.com/documentation/swiftui/view/navigationdestination\(for:destination:\)) modifiers together handle every possible story destination:

-   A `String` value fetches the story by its slug, and a `UUID` value fetches the story by its UUID.
-   A [`Story`](https://storyblok.github.io/storyblok-swift/documentation/storyblokclient/story) value additionally populates the initial value of `StoryView` with the story instance so available content appears immediately while the latest version is fetched.

> [!TIP]
> To navigate to a story from a block view, use a [`NavigationLink`](https://developer.apple.com/documentation/swiftui/navigationlink), or append a value to the navigation path.
> 
> ```swift
> NavigationLink(value: article) { … } // from Story instance
> NavigationLink(value: "articles/my-article") { … } // by slug
> ```

With this approach, the project automatically handles new stories that are added to the space and pushed onto the navigation path.

## Related resources

[Content Delivery API: Retrieve a Single Story](/docs/api/content-delivery/v2/stories/retrieve-a-single-story)

[Apple Developer: NavigationStack Overview](https://developer.apple.com/documentation/swiftui/navigationstack)

[Apple Developer: Associating Navigation Destinations with Data Types](https://developer.apple.com/documentation/swiftui/view/navigationdestination\(for:destination:\))

## Pagination

-   [Previous: Visual Preview in Apple](/docs/guides/apple/visual-preview)
-   [Next: Content Modeling in Apple](/docs/guides/apple/content-modeling)
