Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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,66 @@
//
// ToggleInteractionTests.swift
// OpenSwiftUIInteractionTests

#if OPENSWIFTUI
import OpenSwiftUI
#else
import SwiftUI
#endif
import Testing

@MainActor
@Suite(.serialized, .tags(.interaction))
struct ToggleInteractionTests {
@Test
func repeatedTapsUpdateBinding() async throws {
var values: [Bool] = []
let content = StatefulToggle { values.append($0) }

try await withInteractionTestHost(of: content) { host in
#expect(values.isEmpty)
for count in 1...3 {
try await host.tap()
try await host.waitUntil(values.count >= count)
// Let the native switch finish its animation before the next tap.
try await Task.sleep(for: .milliseconds(600))
}
#expect(values == [true, false, true])
}
}

@Test
func disabledToggleDoesNotUpdateBinding() async throws {
var values: [Bool] = []
let content = StatefulToggle { values.append($0) }
.disabled(true)

try await withInteractionTestHost(of: content) { host in
for _ in 0..<2 {
try await host.tap()
try await Task.sleep(for: .milliseconds(600))
}
#expect(values.isEmpty)
}
}

private struct StatefulToggle: View {
var onChange: (Bool) -> Void
@State private var reset = false

var body: some View {
Toggle("Reset", isOn: Binding(
get: { reset },
set: {
reset = $0
onChange($0)
}
))
#if os(macOS)
.toggleStyle(.switch)
#endif
.labelsHidden()
.fixedSize()
}
}
}
2 changes: 1 addition & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Sources/COpenSwiftUI/Shims/AppKit/AppKit_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ typedef OPENSWIFTUI_ENUM(NSInteger, NSViewVibrantBlendingStyle) {
- (void)showActiveFirstResponderIndication;
- (void)hideActiveFirstResponderIndication;
- (void)_primitiveSetNextKeyView:(nullable NSView *)view OPENSWIFTUI_SWIFT_NAME(_primitiveSetNextKeyView(_:));
- (void)_primitiveSetPreviousKeyView:(nullable NSView *)view OPENSWIFTUI_SWIFT_NAME(_primitiveSetPreviousKeyView(_:));
- (void)_setDefaultKeyViewLoop;
- (nullable NSView *)_recursiveSetDefaultKeyViewLoop;
- (void)_recursiveGatherAllKeyViewCandidatesInArray:(NSMutableArray *)array OPENSWIFTUI_SWIFT_NAME(_recursiveGatherAllKeyViewCandidates(in:));
@end
Expand Down
5 changes: 5 additions & 0 deletions Sources/COpenSwiftUI/Shims/AppKit/NSView_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
@property (nonatomic) BOOL ignoreHitTest_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(ignoreHitTest);
@property (nullable, copy) id<NSContentStyle> contentStyle;
@property NSUserInterfaceLayoutDirection userInterfaceLayoutDirection;
@property (readonly) BOOL _wantsConstraintBasedLayout;

- (void)_setWantsToHostAutolayoutEngine:(BOOL)value;
- (CGSize)_layoutSizeThatFits:(CGSize)size fixedAxes:(unsigned long long)axes;
- (NSLayoutPriority)_priorityForEngineHostConstraints;

- (BOOL)_userInterfaceLayoutDirectionPropagatesToDescendants;
- (void)_setUserInterfaceLayoutDirectionPropagatesToDescendants:(BOOL)propagates;
Expand Down
2 changes: 2 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 Down Expand Up @@ -55,6 +55,7 @@
@end

@interface UIView (OpenSwiftUI_SPI)
- (void)_noteTraitsDidChangeRecursively_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_noteTraitsDidChangeRecursively());
@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;
Expand Down Expand Up @@ -93,6 +94,7 @@
#endif

@interface UIViewController (OpenSwiftUI_SPI)
- (void)_noWarn_setOverrideTraitCollection_openswiftui_safe_wrapper:(UITraitCollection *)traits forChild:(UIViewController *)child OPENSWIFTUI_SWIFT_NAME(_noWarn_setOverrideTraitCollection(_:forChild:));
+ (nullable UIViewController *)viewControllerForView_openswiftui_safe_wrapper:(UIView *)view OPENSWIFTUI_SWIFT_NAME(viewController(for:));
@property (nonatomic, readonly) BOOL _shouldIgnoreChildFocusRegions_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_shouldIgnoreChildFocusRegions);
@property (nonatomic, readonly) BOOL _canShowWhileLocked_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_canShowWhileLocked);
Expand Down
10 changes: 10 additions & 0 deletions Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
}
@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 @@ -55,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]
- (void)_noteTraitsDidChangeRecursively_openswiftui_safe_wrapper {
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_noteTraitsDidChangeRecursively", );
func(self, selector);
}

- (UIResponder *)firstResponder_openswiftui_safe_wrapper {
OPENSWIFTUI_SAFE_WRAPPER_IMP(UIResponder *, @"firstResponder", nil);
return func(self, selector);
Expand Down Expand Up @@ -93,6 +98,11 @@
@end

@implementation UIViewController (OpenSwiftUI_SPI)
- (void)_noWarn_setOverrideTraitCollection_openswiftui_safe_wrapper:(UITraitCollection *)traits forChild:(UIViewController *)child {
OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_noWarn_setOverrideTraitCollection:forChild:", , UITraitCollection *, UIViewController *);
func(self, selector, traits, child);
}

+ (UIViewController *)viewControllerForView_openswiftui_safe_wrapper:(UIView *)view {
OPENSWIFTUI_SAFE_WRAPPER_IMP(UIViewController *, @"viewControllerForView:", nil, UIView *);
return func(self, selector, view);
Expand Down
23 changes: 23 additions & 0 deletions Sources/OpenSwiftUI/Data/Environment/UIKitEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,27 @@ extension EnvironmentValues {
}
}

extension UIView {
func adoptEnvironment(_ environment: EnvironmentValues, hostedSubview: UIView) {
let isEnabled = environment.isEnabled
Update.enqueueAction(reason: nil) {
if self.isUserInteractionEnabled != isEnabled {
self.isUserInteractionEnabled = isEnabled
}
if let control = hostedSubview as? UIControl, control.isEnabled != isEnabled {
control.isEnabled = isEnabled
}
}
let semanticContentAttribute: UISemanticContentAttribute = environment.layoutDirection == .rightToLeft
? .forceRightToLeft : .forceLeftToRight
if hostedSubview.semanticContentAttribute != semanticContentAttribute {
hostedSubview.semanticContentAttribute = semanticContentAttribute
}
if let tint = Semantics.UIKitEnvironmentPrefersTint.isEnabled
? environment.tintColor ?? environment.accentColor
: environment.accentColor {
tintColor = unsafeDowncast(tint.resolve(in: environment).kitColor, to: UIColor.self)
}
}
}
#endif
Loading
Loading