Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a9db17c
feat(focus): add default focus evaluation priority
Kyle-Ye Sep 19, 2026
668aa70
feat(focus): implement default focus evaluation
Kyle-Ye Sep 19, 2026
5206d0c
feat(focus): add focusable view APIs
Kyle-Ye Sep 24, 2026
8c2c39f
feat(focus): implement focusable environment rules
Kyle-Ye Sep 24, 2026
698def2
feat(focus): implement focus item update rules
Kyle-Ye Sep 24, 2026
4b8f557
feat(focus): implement focus effect delegation
Kyle-Ye Sep 24, 2026
366e016
feat(focus): implement macOS focus ring views
Kyle-Ye Sep 25, 2026
00253cf
feat(focus): implement and register focus view graph
Kyle-Ye Sep 25, 2026
94b1631
feat(focus): implement focus store
Kyle-Ye Oct 5, 2026
9f9afaa
feat(focus): implement focus store list
Kyle-Ye Oct 5, 2026
90c2979
feat: add search field state api
Kyle-Ye Oct 5, 2026
5f686e7
feat: add focus bridge api
Kyle-Ye Oct 5, 2026
a891be2
feat(focus): implement focus item behavior
Kyle-Ye Oct 5, 2026
3cda2a1
feat(focus): implement shared focus responder interfaces
Kyle-Ye Oct 5, 2026
957a556
feat(focus): implement and document focused values
Kyle-Ye Oct 5, 2026
0760c35
feat(focus): implement focused bindings and value publishers
Kyle-Ye Oct 5, 2026
ad1ff3a
chore: update focus state binding
Kyle-Ye Oct 5, 2026
b46b195
feat(focus): implement and document focus state
Kyle-Ye Oct 5, 2026
4b48526
feat(focus): implement UIKit hosted focus items
Kyle-Ye Oct 5, 2026
25212f6
fix(focus): restore UIKit focus logging
Kyle-Ye Oct 5, 2026
bda9a81
feat(focus): implement UIViewResponder focus items
Kyle-Ye Oct 5, 2026
8e63e2f
feat(focus): implement AppKit responders and shared view providers
Kyle-Ye Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// UIKitContainerFocusResponderItemBase.h
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#ifndef UIKitContainerFocusResponderItemBase_h
#define UIKitContainerFocusResponderItemBase_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_IOS || OPENSWIFTUI_TARGET_OS_VISION

#import <Foundation/Foundation.h>

OPENSWIFTUI_SWIFT_NAME(UIKitContainerFocusResponderItemBase)
@interface OpenSwiftUIUIKitContainerFocusResponderItemBase : NSObject
@end

#endif

#endif /* UIKitContainerFocusResponderItemBase_h */
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// UIKitContainerFocusResponderItemBase.m
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#import "UIKitContainerFocusResponderItemBase.h"

#if OPENSWIFTUI_TARGET_OS_IOS || OPENSWIFTUI_TARGET_OS_VISION

@implementation OpenSwiftUIUIKitContainerFocusResponderItemBase
@end

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// UIKitFocusableViewResponderItemBase.h
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#ifndef UIKitFocusableViewResponderItemBase_h
#define UIKitFocusableViewResponderItemBase_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_IOS || OPENSWIFTUI_TARGET_OS_VISION

#import <UIKit/UIKit.h>

OPENSWIFTUI_SWIFT_NAME(UIKitFocusableViewResponderItemBase)
@interface OpenSwiftUIUIKitFocusableViewResponderItemBase : UIResponder
@property (nonatomic, readonly, nullable) NSString *openswiftui_focusGroupIdentifier;
@end

#endif

#endif /* UIKitFocusableViewResponderItemBase_h */
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// UIKitFocusableViewResponderItemBase.m
// COpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

#import "UIKitFocusableViewResponderItemBase.h"

#if OPENSWIFTUI_TARGET_OS_IOS || OPENSWIFTUI_TARGET_OS_VISION

@implementation OpenSwiftUIUIKitFocusableViewResponderItemBase

- (NSString *)openswiftui_focusGroupIdentifier {
return nil;
}

- (NSString *)focusGroupIdentifier {
return self.openswiftui_focusGroupIdentifier;
}

@end

#endif
11 changes: 11 additions & 0 deletions Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#import <UIKit/UIKit.h>

#if OPENSWIFTUI_LINK_BACKLIGHTSERVICES
#include <BacklightServices/BLSBacklightFBSSceneEnvironment.h>

Check warning on line 17 in Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h

View workflow job for this annotation

GitHub Actions / Execute tests on iOS (26.6, 2024, 18.5)

missing submodule 'BacklightServices.BLSBacklightFBSSceneEnvironment' [-Wincomplete-umbrella]
#endif

OPENSWIFTUI_ASSUME_NONNULL_BEGIN
Expand All @@ -26,6 +26,15 @@
- (NSComparisonResult)accessibilityCompareGeometry:(NSObject *)element;
@end

@interface NSObject (OpenSwiftUIFocus)
- (void)_requestFocusEffectUpdateToEnvironment:(id<UIFocusEnvironment>)environment OPENSWIFTUI_SWIFT_NAME(_requestFocusEffectUpdate(to:));
@end

@interface UIFocusSystem (OpenSwiftUI_SPI)
- (void)_focusEnvironmentWillDisappear_openswiftui_safe_wrapper:(id<UIFocusEnvironment>)environment OPENSWIFTUI_SWIFT_NAME(_focusEnvironmentWillDisappear(_:));
- (void)_focusEnvironmentDidAppear_openswiftui_safe_wrapper:(id<UIFocusEnvironment>)environment OPENSWIFTUI_SWIFT_NAME(_focusEnvironmentDidAppear(_:));
@end

@interface UIApplication (OpenSwiftUI_SPI)
- (nullable NSString *)_extendLaunchTest;
- (BOOL)_supportsPrintCommand;
Expand All @@ -42,6 +51,7 @@
@end

@interface UIView (OpenSwiftUI_SPI)
@property(nonatomic, readonly, nullable) UIResponder *firstResponder_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(firstResponder);
- (nullable id<UILargeContentViewerItem>)_largeContentViewerItemAtPoint:(CGPoint)point OPENSWIFTUI_SWIFT_NAME(_largeContentViewerItem(at:));
- (instancetype)_initWithLayer:(CALayer *)layer;
- (void)_geometryChanged:(const void *)geometry forAncestor:(nullable UIView *)ancestor;
Expand Down Expand Up @@ -97,6 +107,7 @@
@end

@interface UIWindowScene (OpenSwiftUI_SPI)
@property (nonatomic, readonly, nullable) NSObject *_focusSystemSceneComponent_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_focusSystemSceneComponent);
@property (nonatomic, readonly) UIUserInterfaceStyle _systemUserInterfaceStyle_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_systemUserInterfaceStyle);
#if OPENSWIFTUI_LINK_BACKLIGHTSERVICES
@property (nonatomic, readonly, nullable) BLSBacklightFBSSceneEnvironment *_backlightSceneEnvironment_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_backlightSceneEnvironment);
Expand Down
22 changes: 22 additions & 0 deletions Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,19 @@

#import <objc/runtime.h>

@implementation UIFocusSystem (OpenSwiftUI_SPI)
- (void)_focusEnvironmentWillDisappear_openswiftui_safe_wrapper:(id<UIFocusEnvironment>)environment {
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_focusEnvironmentWillDisappear:", , id<UIFocusEnvironment>);
func(self, selector, environment);
}

- (void)_focusEnvironmentDidAppear_openswiftui_safe_wrapper:(id<UIFocusEnvironment>)environment {
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_focusEnvironmentDidAppear:", , id<UIFocusEnvironment>);
func(self, selector, environment);
}
@end

@implementation UIApplication (OpenSwiftUI_SPI)

Check warning on line 26 in Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m

View workflow job for this annotation

GitHub Actions / Execute tests on iOS (26.6, 2024, 18.5)

method definition for '_supportsPrintCommand' not found [-Wincomplete-implementation]

Check warning on line 26 in Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m

View workflow job for this annotation

GitHub Actions / Execute tests on iOS (26.6, 2024, 18.5)

method definition for '_extendLaunchTest' not found [-Wincomplete-implementation]
- (void)_performBlockAfterCATransactionCommits_openswiftui_safe_wrapper:(void (^)(void))block {
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_performBlockAfterCATransactionCommits:", block(),void (^)(void));
func(self, selector, block);
Expand Down Expand Up @@ -43,7 +55,12 @@
}
@end

@implementation UIView (OpenSwiftUI_SPI)

Check warning on line 58 in Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m

View workflow job for this annotation

GitHub Actions / Execute tests on iOS (26.6, 2024, 18.5)

method definition for '_initWithLayer:' not found [-Wincomplete-implementation]

Check warning on line 58 in Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m

View workflow job for this annotation

GitHub Actions / Execute tests on iOS (26.6, 2024, 18.5)

method definition for '_largeContentViewerItemAtPoint:' not found [-Wincomplete-implementation]
- (UIResponder *)firstResponder_openswiftui_safe_wrapper {
OPENSWIFTUI_SAFE_WRAPPER_IMP(UIResponder *, @"firstResponder", nil);
return func(self, selector);
}

- (BOOL)_shouldAnimatePropertyWithKey_openswiftui_safe_wrapper:(NSString *)key {
OPENSWIFTUI_SAFE_WRAPPER_IMP(BOOL, @"_shouldAnimatePropertyWithKey:", NO, NSString *);
return func(self, selector, key);
Expand Down Expand Up @@ -103,6 +120,11 @@
@end

@implementation UIWindowScene (OpenSwiftUI_SPI)
- (NSObject *)_focusSystemSceneComponent_openswiftui_safe_wrapper {
OPENSWIFTUI_SAFE_WRAPPER_IMP(NSObject *, @"_focusSystemSceneComponent", nil);
return func(self, selector);
}

- (UIUserInterfaceStyle) _systemUserInterfaceStyle_openswiftui_safe_wrapper {
OPENSWIFTUI_SAFE_WRAPPER_IMP(UIUserInterfaceStyle, @"_systemUserInterfaceStyle", UIUserInterfaceStyleUnspecified);
return func(self, selector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,17 @@ public struct AccessibilityFocusState<Value>: DynamicProperty where Value: Hasha
@propertyWrapper
@frozen
public struct Binding {
private var _binding: OpenSwiftUICore.Binding<Value>
@OpenSwiftUICore.Binding
private var binding: Value

init(binding: OpenSwiftUICore.Binding<Value>) {
_binding = binding
}

/// The underlying value referenced by the bound property.
public var wrappedValue: Value {
get { _binding.wrappedValue }
nonmutating set { _binding.wrappedValue = newValue }
get { binding }
nonmutating set { binding = newValue }
}

/// The currently focused element.
Expand Down
27 changes: 27 additions & 0 deletions Sources/OpenSwiftUI/Event/Focus/DefaultFocus.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// DefaultFocus.swift
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: WIP
// ID: 1F8B69996BE941D510140AD6558D8844 (SwiftUI)

import OpenAttributeGraphShims
@_spi(ForOpenSwiftUIOnly)
import OpenSwiftUICore

// MARK: - FocusScopesKey

struct FocusScopesKey: EnvironmentKey {
static var defaultValue: [Namespace.ID] { [] }
}

extension CachedEnvironment.ID {
fileprivate static let focusScopes: CachedEnvironment.ID = .init()
}

extension _GraphInputs {
var focusScopes: Attribute<[Namespace.ID]> {
mapEnvironment(id: .focusScopes) { $0[FocusScopesKey.self] }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// DefaultFocusEvaluationPriority.swift
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: Complete

/// Prioritizations for default focus preferences when evaluating where
/// to move focus in different circumstances.
@available(OpenSwiftUI_v4_0, *)
public struct DefaultFocusEvaluationPriority: Sendable {
let rawValue: Int

/// Use the default focus preference when focus moves into the affected
/// branch automatically, but ignore it when the movement is driven by a
/// user-initiated navigation command.
public static let automatic: DefaultFocusEvaluationPriority = .init(rawValue: 0)

/// Always use the default focus preference when focus moves into the
/// affected branch.
public static let userInitiated: DefaultFocusEvaluationPriority = .init(rawValue: 1)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,61 @@
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: WIP
// Status: Complete
// ID: 9993DE8943AC71AF21213EB6FCE0DB61 (SwiftUI?)

struct EvaluateDefaultFocusAction {}
#if os(macOS)
import OpenSwiftUICore

// MARK: - EvaluateDefaultFocusAction

struct EvaluateDefaultFocusAction: Hashable {
var namespace: Namespace.ID
var priority: DefaultFocusEvaluationPriority
var onEvaluate: (() -> (any BaseFocusResponder)?)?

static func == (lhs: Self, rhs: Self) -> Bool {
lhs.namespace == rhs.namespace
}

func hash(into hasher: inout Hasher) {
hasher.combine(namespace)
}
}

// MARK: - DefaultFocusStateModifier

struct DefaultFocusStateModifier<A>: ViewModifier where A: Hashable {
var binding: FocusState<A>.Binding
var value: A
var priority: DefaultFocusEvaluationPriority
@Namespace var namespace

init(binding: FocusState<A>.Binding, value: A, priority: DefaultFocusEvaluationPriority) {
self.binding = binding
self.value = value
self.priority = priority
}

func body(content: Content) -> some View {
content.environment(\.evaluateDefaultFocus, EvaluateDefaultFocusAction(
namespace: namespace,
priority: priority,
onEvaluate: { binding.location.findEntry(with: value)?.responder }
))
}
}

// MARK: - EnvironmentValues + Default Focus

extension EnvironmentValues {
var evaluateDefaultFocus: EvaluateDefaultFocusAction? {
get { self[EvaluateDefaultFocusKey.self] }
set { self[EvaluateDefaultFocusKey.self] = newValue }
}

private struct EvaluateDefaultFocusKey: EnvironmentKey {
static var defaultValue: EvaluateDefaultFocusAction? { nil }
}
}
#endif
68 changes: 68 additions & 0 deletions Sources/OpenSwiftUI/Event/Focus/FocusBridge.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// FocusBridge.swift
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: WIP

@_spi(ForOpenSwiftUIOnly)
import OpenSwiftUICore

// MARK: - FocusHost

protocol FocusHost: ViewRendererHost {
var focusedItem: FocusItem? { get }
var focusedValues: FocusedValues { get set }
var currentAccessibilityFocusStore: AccessibilityFocusStore { get set }

func focus(item: FocusItem)
func focusDidChange()
}

// MARK: - FocusBridge [TBA]

class FocusBridge {
var flags: Flags

#if os(macOS)
lazy var navigator: FocusNavigator = .init(bridge: self)
#endif

// MARK: - Initialization [TBA]

init() {
_openSwiftUIUnimplementedFailure()
}

// MARK: - Platform focus [TBA]

var host: (PlatformView & FocusBridgeProvider & FocusHost)? {
_openSwiftUIUnimplementedFailure()
}

func moveFocus(to item: FocusItem, designatedPlatformResponder: PlatformView?) {
_openSwiftUIUnimplementedFailure()
}

var canAcceptFocus: Bool {
#if os(macOS)
flags.contains(.canAcceptFocus)
#else
_openSwiftUIUnimplementedFailure()
#endif
}

// MARK: - Flags

struct Flags: OptionSet {
var rawValue: Int

static let canAcceptFocus: Flags = .init(rawValue: 1 << 1)
}
}

// MARK: - FocusBridgeProvider

protocol FocusBridgeProvider: AnyObject {
var focusBridge: FocusBridge { get }
}
Loading
Loading