Simple solution for visibilityPriority for toolbar items.

The iPhone Duo provides varying space for toolbar items based on screen orientation. One approach uses .visibilityPriority(_) to keep higher-priority items visible longer as the window shrinks and items go to the overflow menu. For instance:

.toolbar {
                ToolbarItem {
                    SecondaryControl()
                }
                ToolbarItem {
                    PrimaryControl()
                }
                .visibilityPriority(.high)
}

The problem is that .visibilityPriority(_) requires iOS 27 or later, while typical apps aim for older systems such as iOS 26.

What is your solution to keep the code simple?

I share the solution I use today that can be used with iOS 27 with the priorities while still working as before on older iOS.

Part 1/2

//
//  ToolbarContentExtension.swift
//
//  Created by L'Appli en Rose, Paris on 23/09/2026.
//  No Rights Reserved, Free to use
//  SPDX-License-Identifier: CC0-1.0

import SwiftUI

extension ToolbarContent {

    /// Defines the visibility priority for a toolbar item after iOS 27 / iPadOS27 and visionOS 27.
    /// **Does nothing on previous versions.**
    ///
    /// When toolbar space is limited, items with a lower priority
    /// move into the overflow menu before items with a higher priority.
    /// The default is ``ToolbarItemVisibilityPriorityIfAvailable/automatic``.
    ///
    /// For example, an important control can appear at the trailing edge
    /// of the toolbar, but still be shown as the window is made smaller:
    ///
    ///     struct RootView: View {
    ///         var body: some View {
    ///             ContentView()
    ///                 .toolbar {
    ///                     ToolbarItem {
    ///                         SecondaryControl()
    ///                     }
    ///                     ToolbarItem {
    ///                         PrimaryControl()
    ///                     }
    ///                     .visibilityPriorityIfAvailable(.high)
    ///                 }
    ///         }
    ///     }
    ///
    /// - Parameter priority: The visibility priority for this toolbar
    ///   item.
    @MainActor @preconcurrency func visibilityPriorityIfAvailable(_ priority: ToolbarItemVisibilityPriorityIfAvailable) -> some ToolbarContent {
        if #available(iOS 27.0, macOS 26.1, watchOS 27.0, tvOS 27.0, visionOS 27.0, *) {
            let newLevel: ToolbarItemVisibilityPriority
            switch priority.level {
            case .low: newLevel = .low
            case .high: newLevel = .high
            case .lowerThanLow: newLevel = ToolbarItemVisibilityPriority(lowerThan: .low)
            case .lowerThanAutomatic: newLevel = ToolbarItemVisibilityPriority(lowerThan: .automatic)
            case .lowerThanHigh: newLevel = ToolbarItemVisibilityPriority(lowerThan: .high)
            case .higherThanLow: newLevel = ToolbarItemVisibilityPriority(higherThan: .low)
            case .higherThanAutomatic: newLevel = ToolbarItemVisibilityPriority(higherThan: .automatic)
            case .higherThanHigh: newLevel = ToolbarItemVisibilityPriority(higherThan: .high)
            default: newLevel = .automatic
            }
            return self.visibilityPriority(newLevel)
        } else { // Prior to OS 27, no modifcation to the ToolbarContent
            return self
        }
    }
}

Part 2/2

/// A value that defines the visibility priority of a toolbar item after iOS 27/iPadOS27 and visionOS 27.
/// The value is ignored on previous versions.
///
/// When a toolbar runs out of space, it moves items into an overflow
/// menu. Visibility priority controls the order in which that happens:
/// items with a lower priority move first, keeping higher-priority
/// items visible longer as the window shrinks.
///
/// Use values of this type with the
/// ``ToolbarContent/visibilityPriorityIfAvailable(_:)`` modifier. For example,
/// to keep a share button visible longer than an archive button:
///
///     struct RootView: View {
///         var body: some View {
///             ContentView()
///                 .toolbar {
///                     ToolbarItem {
///                         SecondaryControl()
///                     }
///                     ToolbarItem {
///                         PrimaryControl()
///                     }
///                     .visibilityPriorityIfAvailable(.high)
///                 }
///         }
///     }
struct ToolbarItemVisibilityPriorityIfAvailable : Hashable, Sendable {
    fileprivate let level: VisibilityPriorityIfAvailable
    
    /// The default priority that lets the system determine the item's
    /// visibility in the toolbar.
    static let automatic = ToolbarItemVisibilityPriorityIfAvailable(.automatic)
    /// A priority that moves the item to the overflow menu before
    /// items with the default or high priority.
    ///
    /// Use for secondary actions, such as archive or delete, that
    /// don't need persistent visibility in the toolbar.
    /// > Warning : Prior to OS 27, it is ignored.
    static let low = ToolbarItemVisibilityPriorityIfAvailable(.low)
    /// A priority that keeps the item in the toolbar longer than
    /// items with the default or low priority.
    ///
    /// Use for frequently used actions that need to stay visible as
    /// the toolbar shrinks.
    /// > Warning : Prior to OS 27, it is ignored.
    static let high = ToolbarItemVisibilityPriorityIfAvailable(.high)
    
    /// Internal initializer.
    /// - Parameter level: <#level description#>
    private init(_ level: VisibilityPriorityIfAvailable) {
        self.level = level
    }
    
    /// Creates a priority lower than the specified value.
    ///
    /// The priority is lower than `other` but doesn't cross below
    /// the next lower system priority. For example,
    /// `ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)` returns a value
    /// that is less than `.high` but greater than `.automatic`.
    ///
    /// Priorities created with the same base value are equal:
    ///
    ///     let x = ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)
    ///     let y = ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)
    ///     x == y // true
    ///
    /// > Warning : calling this initializer with a value different from .low .automatic or .high returns the same
    /// other value. This is a limitation of this transitional code.
    init(lowerThan other: ToolbarItemVisibilityPriorityIfAvailable) {
        switch other.level {
        case .low:
            self.level = .lowerThanLow
        case .automatic:
            self.level = .lowerThanAutomatic
        case .high:
            self.level = .lowerThanHigh
        default: // We don't manage other complex cases, we let them at the same level
            self.level = other.level
        }
    }
    
    /// Creates a priority higher than the specified value.
    ///
    /// The priority is higher than `other` but doesn't cross above
    /// the next higher system priority. For example,
    /// `ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)` returns a
    /// value that is greater than `.high`.
    ///
    /// Priorities created with the same base value are equal:
    ///
    ///     let x = ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)
    ///     let y = ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)
    ///     x == y // true
    ///
    /// > Warning : calling this initializer with a value different from .low .automatic or .high returns the same
    /// other value. This is a limitation of this transitional code.
    init(higherThan other: ToolbarItemVisibilityPriorityIfAvailable) {
        switch other.level {
        case .low:
            self.level = .higherThanLow
        case .automatic:
            self.level = .higherThanAutomatic
        case .high:
            self.level = .higherThanHigh
        default:  // We don't manage other complex cases, we let them at the same level
            self.level = other.level
        }
    }

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    public static func == (a: ToolbarItemVisibilityPriorityIfAvailable, b: ToolbarItemVisibilityPriorityIfAvailable) -> Bool {
        return a.level == b.level
    }

}

/// Enumeration of the managed level values for simpler code.
fileprivate enum VisibilityPriorityIfAvailable: Int {
    case low = 100
    case automatic = 200
    case high = 300
    case lowerThanLow = 99
    case higherThanLow = 101
    case lowerThanAutomatic = 199
    case higherThanAutomatic = 201
    case lowerThanHigh = 299
    case higherThanHigh = 301
}

Upgraded version that can be used with iOS 27 with the priorities while still working as before on older iOS. Support for visionOS, watchOS and tvOS is updated by removing features they don't have: .low, .high, lowerThan and higherThan. On those platforms, .automatic is the only supported level.

Part 1/2

//
//  ToolbarContentExtension.swift
//
//  Created by L'Appli en Rose, Paris on 23/09/2026.
//  No Rights Reserved, Free to use
//  SPDX-License-Identifier: CC0-1.0
//  V1.1 Matching support for visionOS, watchOS and tvOS

import SwiftUI

extension ToolbarContent {

    /// Defines the visibility priority for a toolbar item after iOS 27 / iPadOS27 and visionOS 27.
    /// **Does nothing on previous versions.**
    ///
    /// When toolbar space is limited, items with a lower priority
    /// move into the overflow menu before items with a higher priority.
    /// The default is ``ToolbarItemVisibilityPriorityIfAvailable/automatic``.
    ///
    /// For example, an important control can appear at the trailing edge
    /// of the toolbar, but still be shown as the window is made smaller:
    ///
    ///     struct RootView: View {
    ///         var body: some View {
    ///             ContentView()
    ///                 .toolbar {
    ///                     ToolbarItem {
    ///                         SecondaryControl()
    ///                     }
    ///                     ToolbarItem {
    ///                         PrimaryControl()
    ///                     }
    ///                     .visibilityPriorityIfAvailable(.high)
    ///                 }
    ///         }
    ///     }
    ///
    /// - Parameter priority: The visibility priority for this toolbar
    ///   item.
    @MainActor @preconcurrency func visibilityPriorityIfAvailable(_ priority: ToolbarItemVisibilityPriorityIfAvailable) -> some ToolbarContent {
        if #available(iOS 27.0, macOS 26.1, watchOS 27.0, tvOS 27.0, visionOS 27.0, *) {
            let newLevel: ToolbarItemVisibilityPriority
            switch priority.level {
#if os(iOS) || os(macOS) // levels only on iOS/iPadOS, Mac Catalyst and macOS
            case .low: newLevel = .low
            case .high: newLevel = .high
            case .lowerThanLow: newLevel = ToolbarItemVisibilityPriority(lowerThan: .low)
            case .lowerThanAutomatic: newLevel = ToolbarItemVisibilityPriority(lowerThan: .automatic)
            case .lowerThanHigh: newLevel = ToolbarItemVisibilityPriority(lowerThan: .high)
            case .higherThanLow: newLevel = ToolbarItemVisibilityPriority(higherThan: .low)
            case .higherThanAutomatic: newLevel = ToolbarItemVisibilityPriority(higherThan: .automatic)
            case .higherThanHigh: newLevel = ToolbarItemVisibilityPriority(higherThan: .high)
#endif
            default: newLevel = .automatic
            }
            return self.visibilityPriority(newLevel)
        } else { // Prior to OS 27, no modifcation to the ToolbarContent
            return self
        }
    }

}

Version 1.1

Part 2/2

//  V1.1 Matching support for visionOS, watchOS and tvOS

/// A value that defines the visibility priority of a toolbar item after iOS 27/iPadOS27 and visionOS 27.
/// The value is ignored on previous versions.
///
/// When a toolbar runs out of space, it moves items into an overflow
/// menu. Visibility priority controls the order in which that happens:
/// items with a lower priority move first, keeping higher-priority
/// items visible longer as the window shrinks.
///
/// Use values of this type with the
/// ``ToolbarContent/visibilityPriorityIfAvailable(_:)`` modifier. For example,
/// to keep a share button visible longer than an archive button:
///
///     struct RootView: View {
///         var body: some View {
///             ContentView()
///                 .toolbar {
///                     ToolbarItem {
///                         SecondaryControl()
///                     }
///                     ToolbarItem {
///                         PrimaryControl()
///                     }
///                     .visibilityPriorityIfAvailable(.high)
///                 }
///         }
///     }
struct ToolbarItemVisibilityPriorityIfAvailable : Hashable, Sendable {
    fileprivate let level: VisibilityPriorityIfAvailable
    
    /// The default priority that lets the system determine the item's
    /// visibility in the toolbar.
    static let automatic = ToolbarItemVisibilityPriorityIfAvailable(.automatic)
    /// A priority that moves the item to the overflow menu before
    /// items with the default or high priority.
    ///
    /// Use for secondary actions, such as archive or delete, that
    /// don't need persistent visibility in the toolbar.
    /// > Warning : Prior to OS 27, it is ignored.
    @available(watchOS, unavailable)
    @available(tvOS, unavailable)
    @available(visionOS, unavailable)
    static let low = ToolbarItemVisibilityPriorityIfAvailable(.low)
    /// A priority that keeps the item in the toolbar longer than
    /// items with the default or low priority.
    ///
    /// Use for frequently used actions that need to stay visible as
    /// the toolbar shrinks.
    /// > Warning : Prior to OS 27, it is ignored.
    @available(watchOS, unavailable)
    @available(tvOS, unavailable)
    @available(visionOS, unavailable)
    static let high = ToolbarItemVisibilityPriorityIfAvailable(.high)
    
    /// Internal initializer.
    /// - Parameter level: <#level description#>
    private init(_ level: VisibilityPriorityIfAvailable) {
        self.level = level
    }
    
    /// Creates a priority lower than the specified value.
    ///
    /// The priority is lower than `other` but doesn't cross below
    /// the next lower system priority. For example,
    /// `ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)` returns a value
    /// that is less than `.high` but greater than `.automatic`.
    ///
    /// Priorities created with the same base value are equal:
    ///
    ///     let x = ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)
    ///     let y = ToolbarItemVisibilityPriorityIfAvailable(lowerThan: .high)
    ///     x == y // true
    ///
    /// > Warning : calling this initializer with a value different from .low .automatic or .high returns the same
    /// other value. This is a limitation of this transitional code.
    @available(watchOS, unavailable)
    @available(tvOS, unavailable)
    @available(visionOS, unavailable)
    init(lowerThan other: ToolbarItemVisibilityPriorityIfAvailable) {
        switch other.level {
        case .low:
            self.level = .lowerThanLow
        case .automatic:
            self.level = .lowerThanAutomatic
        case .high:
            self.level = .lowerThanHigh
        default: // We don't manage other complex cases, we let them at the same level
            self.level = other.level
        }
    }
    
    /// Creates a priority higher than the specified value.
    ///
    /// The priority is higher than `other` but doesn't cross above
    /// the next higher system priority. For example,
    /// `ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)` returns a
    /// value that is greater than `.high`.
    ///
    /// Priorities created with the same base value are equal:
    ///
    ///     let x = ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)
    ///     let y = ToolbarItemVisibilityPriorityIfAvailable(higherThan: .high)
    ///     x == y // true
    ///
    /// > Warning : calling this initializer with a value different from .low .automatic or .high returns the same
    /// other value. This is a limitation of this transitional code.
    @available(watchOS, unavailable)
    @available(tvOS, unavailable)
    @available(visionOS, unavailable)
    init(higherThan other: ToolbarItemVisibilityPriorityIfAvailable) {
        switch other.level {
        case .low:
            self.level = .higherThanLow
        case .automatic:
            self.level = .higherThanAutomatic
        case .high:
            self.level = .higherThanHigh
        default:  // We don't manage other complex cases, we let them at the same level
            self.level = other.level
        }
    }

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    public static func == (a: ToolbarItemVisibilityPriorityIfAvailable, b: ToolbarItemVisibilityPriorityIfAvailable) -> Bool {
        return a.level == b.level
    }
}

/// Enumeration of the managed level values for simpler code.
fileprivate enum VisibilityPriorityIfAvailable: Int {
    case low = 100
    case automatic = 200
    case high = 300
    case lowerThanLow = 99
    case higherThanLow = 101
    case lowerThanAutomatic = 199
    case higherThanAutomatic = 201
    case lowerThanHigh = 299
    case higherThanHigh = 301
}
Simple solution for visibilityPriority for toolbar items.
 
 
Q