---
title: Integrating with SwiftUI
description: PingOne Advanced Identity Cloud PingAM iOS
component: orchsdks
page_id: orchsdks:journey:usage/ios/07-integrating-with-swiftui
canonical_url: https://developer.pingidentity.com/orchsdks/journey/usage/ios/07-integrating-with-swiftui.html
revdate: Tue, 2 Dec 2025 16:32:08 +0000
section_ids:
  viewmodel: ViewModel
  view_swiftui: View (SwiftUI)
---

# Integrating with SwiftUI

[icon: circle-check, set=far]PingOne Advanced Identity Cloud [icon: circle-check, set=far]PingAM [icon: apple, set=fab]iOS

If your application uses SwiftUI, you can seamlessly integrate the **Journey** module by using an `ObservableObject` to manage the UI state:

## ViewModel

Using the Journey module with a SwiftUI ObservableObject

```swift
@MainActor
class JourneyViewModel: ObservableObject {
    /// Published property that holds the current state node data.
    @Published public var state: JourneyState = JourneyState()
    /// Published property to track whether the view is currently loading.
    @Published public var isLoading: Bool = false

    var journey: Journey

    /// Initializes the view model and starts the Journey orchestration process.
    init() {
        Task {
            await startJourney()
        }
    }

    public func initializeJourney() {
        journey = Journey.createJourney { config in
            let currentConfig = ConfigurationManager.shared.currentConfigurationViewModel
            config.serverUrl = currentConfig?.serverUrl
            config.realm = currentConfig?.realm ?? "root"
            config.cookie = currentConfig?.cookieName ?? ""
            config.module(PingJourney.OidcModule.config) { oidcValue in
                oidcValue.clientId = currentConfig?.clientId ?? ""
                oidcValue.scopes = Set<String>(currentConfig?.scopes ?? [])
                oidcValue.redirectUri = currentConfig?.redirectUri ?? ""
                oidcValue.discoveryEndpoint = currentConfig?.discoveryEndpoint ?? ""
            }
        }
    }

    /// Starts the Journey orchestration process.
    /// - Sets the initial node and updates the `data` property with the starting node.
    public func startJourney() async {

        await MainActor.run {
            isLoading = true
        }

        let next = await journey.start("Login") { options in
            options.forceAuth = false
            options.noSession = false
        }

        await MainActor.run {
            self.state = JourneyState(node: next)
            isLoading = false
        }
    }

    /// Advances to the next node in the orchestration process.
    /// - Parameter node: The current node to progress from.
    public func next(node: Node) async {
        await MainActor.run {
            isLoading = true
        }
        if let current = node as? ContinueNode {
            // Retrieves the next node in the flow.
            let next = await current.next()
            await MainActor.run {
                self.state = JourneyState(node: next)
                isLoading = false
            }
        }
    }

    public func refresh() {
        state = JourneyState(node: state.node)
    }
}
```

## View (SwiftUI)

Using the Journey module with a SwiftUI view

```swift
import SwiftUI

struct JourneyView: View {
    /// The view model that manages the Davinci flow logic.
    @StateObject private var journeyViewModel = JourneyViewModel()
    /// A binding to the navigation stack path.
    @Binding var path: [String]

    var body: some View {
        ZStack {
            ScrollView {
                VStack {
                    Spacer()
                    // Handle different types of nodes in the Journey.
                    switch journeyViewModel.state.node {
                    case let continueNode as ContinueNode:
                        // Display the callback view for the next node.
                        CallbackView(journeyViewModel: journeyViewModel, node: continueNode)
                    case let errorNode as ErrorNode:
                        // Handle server-side errors (e.g., invalid credentials)
                        // Display error to the user
                        ErrorNodeView(node: errorNode)
                        if let nextNode = errorNode.continueNode {
                            CallbackView(journeyViewModel: journeyViewModel, node: nextNode)
                        }
                    case let failureNode as FailureNode:
                        ErrorView(message: failureNode.cause.localizedDescription)
                    case is SuccessNode:
                        // Authentication successful, retrieve the session
                        VStack{}.onAppear {
                            path.removeLast()
                            path.append("Token")
                        }
                    default:
                        EmptyView()
                    }
                }
            }
        }
    }
}
```
