diff --git a/Sources/OpenSwiftUI/Integration/Render/UIKit/UIViewPlatformViewDefinition.swift b/Sources/OpenSwiftUI/Integration/Render/UIKit/UIViewPlatformViewDefinition.swift index ed994d3e..4dd84a15 100644 --- a/Sources/OpenSwiftUI/Integration/Render/UIKit/UIViewPlatformViewDefinition.swift +++ b/Sources/OpenSwiftUI/Integration/Render/UIKit/UIViewPlatformViewDefinition.swift @@ -52,17 +52,18 @@ final class UIViewPlatformViewDefinition: PlatformViewDefinition, @unchecked Sen if kind != .platformView && kind != .platformGroup { view.autoresizesSubviews = false if !kind.isContainer { - // view._setFocusInteractionEnabled = false + view._setFocusInteractionEnabled(false) } } view.layer.anchorPoint = .zero switch kind { case .color, .image, .shape: - // view.layer.setAllowsEdgeAntialiasing = true + view.layer.setAllowsEdgeAntialiasing(true) break case .geometry, .projection, .affine3D, .mask, .platformEffect: - // view.layer.setAllowsGroupOpacity = false - // view.layer.setAllowsGroupBlending = false + let layer = view.layer + layer.setAllowsGroupOpacity(false) + layer.setAllowsGroupBlending(false) break default: break diff --git a/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.h b/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.h index f43f279a..5e83db1f 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.h +++ b/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.h @@ -16,9 +16,9 @@ OPENSWIFTUI_ASSUME_NONNULL_BEGIN @interface NSApplication (OpenSwiftUI_SPI) -- (void)startedTest:(nullable NSString *)name; -- (void)finishedTest:(nullable NSString *)name; -- (void)failedTest:(nullable NSString *)name withFailure:(nullable NSError*)failure; +- (void)startedTest_openswiftui_safe_wrapper:(nullable NSString *)name OPENSWIFTUI_SWIFT_NAME(startedTest(_:)); +- (void)finishedTest_openswiftui_safe_wrapper:(nullable NSString *)name OPENSWIFTUI_SWIFT_NAME(finishedTest(_:)); +- (void)failedTest_openswiftui_safe_wrapper:(nullable NSString *)name withFailure:(nullable NSError*)failure OPENSWIFTUI_SWIFT_NAME(failedTest(_:withFailure:)); @end OPENSWIFTUI_ASSUME_NONNULL_END diff --git a/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.m b/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.m index 84e89901..df9ed293 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.m +++ b/Sources/OpenSwiftUI_SPI/Shims/AppKit/AppKit_Private.m @@ -5,4 +5,28 @@ // Status: WIP #import "AppKit_Private.h" +#import "../OpenSwiftUIShims.h" + +#if __has_include() + +#import + +@implementation NSApplication (OpenSwiftUI_SPI) +- (void)startedTest_openswiftui_safe_wrapper:(NSString *)name { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"startedTest:", , NSString *); + func(self, selector, name); +} + +- (void)finishedTest_openswiftui_safe_wrapper:(NSString *)name { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"finishedTest:", , NSString *); + func(self, selector, name); +} + +- (void)failedTest_openswiftui_safe_wrapper:(NSString *)name withFailure:(NSError *)failure { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"failedTest:withFailure:", , NSString *, NSError *); + func(self, selector, name, failure); +} +@end + +#endif /* AppKit.h */ diff --git a/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.h b/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.h index d8bc54fc..1b7c7027 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.h +++ b/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.h @@ -14,7 +14,11 @@ OPENSWIFTUI_ASSUME_NONNULL_BEGIN @interface CALayer (OpenSwiftUI_SPI) -@property (nonatomic, assign, readonly) BOOL openSwiftUI_hasBeenCommitted OPENSWIFTUI_SWIFT_NAME(hasBeenCommitted); +@property (nonatomic, assign, readonly) BOOL hasBeenCommitted_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(hasBeenCommitted); +- (void)setAllowsEdgeAntialiasing_openswiftui_safe_wrapper:(BOOL)allows OPENSWIFTUI_SWIFT_NAME(setAllowsEdgeAntialiasing(_:)); +- (void)setAllowsGroupOpacity_openswiftui_safe_wrapper:(BOOL)allows OPENSWIFTUI_SWIFT_NAME(setAllowsGroupOpacity(_:)); +- (void)setAllowsGroupBlending_openswiftui_safe_wrapper:(BOOL)allows OPENSWIFTUI_SWIFT_NAME(setAllowsGroupBlending(_:)); + @property (nonatomic, assign) uint64_t openSwiftUI_viewTestProperties; @end diff --git a/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.m b/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.m index d3ca1284..c81a74fa 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.m +++ b/Sources/OpenSwiftUI_SPI/Shims/CoreAnimation/CoreAnimation_Private.m @@ -6,52 +6,39 @@ #if __has_include() +#import "../OpenSwiftUIShims.h" #import @implementation CALayer (OpenSwiftUI_SPI) -- (BOOL)openSwiftUI_hasBeenCommitted { - typedef BOOL (*Func)(CALayer *, SEL); - SEL selector = NSSelectorFromString(@"hasBeenCommitted"); - Func func = nil; - if ([self respondsToSelector:selector]) { - IMP impl = class_getMethodImplementation([self class], selector); - func = (Func)impl; - } - if (func == nil) { - return NO; - } +- (BOOL)hasBeenCommitted_openswiftui_safe_wrapper { + OPENSWIFTUI_SAFE_WRAPPER_IMP(BOOL, @"hasBeenCommitted", NO); return func(self, selector); } -- (uint64_t)openSwiftUI_viewTestProperties { - typedef uint64_t (*Func)(CALayer *, SEL); - SEL selector = NSSelectorFromString(@"swiftUI_viewTestProperties"); - Func func = nil; - if ([self respondsToSelector:selector]) { - IMP impl = class_getMethodImplementation([self class], selector); - func = (Func)impl; - } - if (func == nil) { - return 0; - } - return func(self, selector); +- (void)setAllowsEdgeAntialiasing_openswiftui_safe_wrapper:(BOOL)allows { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"setAllowsEdgeAntialiasing:", , BOOL); + func(self, selector, allows); +} + +- (void)setAllowsGroupOpacity_openswiftui_safe_wrapper:(BOOL)allows { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"setAllowsGroupOpacity:", , BOOL); + func(self, selector, allows); } -- (void)setOpenSwiftUI_viewTestProperties:(uint64_t)viewTestProperties { - typedef void (*Func)(CALayer *, SEL, uint64_t); - SEL selector = NSSelectorFromString(@"setSwiftUI_viewTestProperties:"); - Func func = nil; - if ([self respondsToSelector:selector]) { - IMP impl = class_getMethodImplementation([self class], selector); - func = (Func)impl; - } - if (func == nil) { - return; - } - return func(self, selector, viewTestProperties); +- (void)setAllowsGroupBlending_openswiftui_safe_wrapper:(BOOL)allows { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"setAllowsGroupBlending:", , BOOL); + func(self, selector, allows); } +- (uint64_t)openSwiftUI_viewTestProperties { + NSNumber *properties = [self valueForKey:@"_viewTestProperties"]; + return properties.integerValue; +} + +- (void)setOpenSwiftUI_viewTestProperties:(uint64_t)properties { + [self setValue:[NSNumber numberWithUnsignedLongLong:properties] forKey:@"_viewTestProperties"]; +} @end #endif /* CoreAnimation.h */ diff --git a/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.h b/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.h new file mode 100644 index 00000000..f587ccb4 --- /dev/null +++ b/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.h @@ -0,0 +1,42 @@ +// +// OpenSwiftUIShims.h +// OpenSwiftUI_SPI +// + +#ifndef OpenSwiftUIShims_h +#define OpenSwiftUIShims_h + +#include "OpenSwiftUIBase.h" + +#if OPENSWIFTUI_TARGET_OS_DARWIN + +#include + +OPENSWIFTUI_ASSUME_NONNULL_BEGIN + +OPENSWIFTUI_EXPORT +os_log_t openSwiftUIShimsLog(void); + +#define OPENSWIFTUI_SHIMS_LOG_ERROR(fmt, ...) os_log_error(openSwiftUIShimsLog(), fmt, ##__VA_ARGS__) + +#define OPENSWIFTUI_SAFE_WRAPPER_IMP(ReturnType, SelectorName, DefaultReturnValue, ...) \ + typedef ReturnType (*Func)(id, SEL, ##__VA_ARGS__); \ + SEL selector = NSSelectorFromString(SelectorName); \ + Func func = nil; \ + if ([self respondsToSelector:selector]) { \ + IMP impl = class_getMethodImplementation([self class], selector); \ + func = (Func)impl; \ + } else { \ + OPENSWIFTUI_SHIMS_LOG_ERROR("%@ can't respond to selector %@", NSStringFromClass([self class]), NSStringFromSelector(selector)); \ + } \ + if (func == nil) { \ + OPENSWIFTUI_SHIMS_LOG_ERROR("%@ can't get method implementation for selector %@", NSStringFromClass([self class]), NSStringFromSelector(selector)); \ + return DefaultReturnValue; \ + } + + +OPENSWIFTUI_ASSUME_NONNULL_END + +#endif /* OPENSWIFTUI_TARGET_OS_DARWIN */ + +#endif /* OpenSwiftUIShims_h */ diff --git a/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.m b/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.m new file mode 100644 index 00000000..ff5a8958 --- /dev/null +++ b/Sources/OpenSwiftUI_SPI/Shims/OpenSwiftUIShims.m @@ -0,0 +1,19 @@ +// +// OpenSwiftUIShims.m +// OpenSwiftUI_SPI +// + +#import "OpenSwiftUIShims.h" + +#if OPENSWIFTUI_TARGET_OS_DARWIN + +os_log_t openSwiftUIShimsLog(void) { + static dispatch_once_t onceToken; + static os_log_t log = NULL; + dispatch_once(&onceToken, ^{ + log = os_log_create("org.OpenSwiftUIProject.OpenSwiftUI", "Shims"); + }); + return log; +} + +#endif /* OPENSWIFTUI_TARGET_OS_DARWIN */ diff --git a/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.h b/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.h index 9bac465e..c2794e7d 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.h +++ b/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.h @@ -17,20 +17,21 @@ OPENSWIFTUI_ASSUME_NONNULL_BEGIN @interface UIApplication (OpenSwiftUI_SPI) // Test API -- (void)startedTest:(nullable NSString *)name; -- (void)finishedTest:(nullable NSString *)name; -- (void)failedTest:(nullable NSString *)name withFailure:(nullable NSError*)failure; -- (nullable NSString *)_launchTestName; +- (void)startedTest_openswiftui_safe_wrapper:(nullable NSString *)name OPENSWIFTUI_SWIFT_NAME(startedTest(_:)); +- (void)finishedTest_openswiftui_safe_wrapper:(nullable NSString *)name OPENSWIFTUI_SWIFT_NAME(finishedTest(_:)); +- (void)failedTest_openswiftui_safe_wrapper:(nullable NSString *)name withFailure:(nullable NSError*)failure OPENSWIFTUI_SWIFT_NAME(failedTest(_:withFailure:)); +- (nullable NSString *)_launchTestName_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_launchTestName()); - (void)_performBlockAfterCATransactionCommits_openswiftui_safe_wrapper:(void (^)(void))block OPENSWIFTUI_SWIFT_NAME(_performBlockAfterCATransactionCommits(_:)); @end @interface UIView (OpenSwiftUI_SPI) -- (BOOL)_shouldAnimatePropertyWithKey:(NSString *)key; +- (BOOL)_shouldAnimatePropertyWithKey_openswiftui_safe_wrapper:(NSString *)key OPENSWIFTUI_SWIFT_NAME(_shouldAnimateProperty(withKey:)); +- (void)_setFocusInteractionEnabled_openswiftui_safe_wrapper:(BOOL)enabled OPENSWIFTUI_SWIFT_NAME(_setFocusInteractionEnabled(_:)); @end @interface UIViewController (OpenSwiftUI_SPI) -@property (nonatomic, readonly) BOOL _canShowWhileLocked; +@property (nonatomic, readonly) BOOL _canShowWhileLocked_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_canShowWhileLocked); @end OPENSWIFTUI_EXPORT diff --git a/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.m b/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.m index 08a8869c..0624d3b1 100644 --- a/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.m +++ b/Sources/OpenSwiftUI_SPI/Shims/UIKit/UIKit_Private.m @@ -5,6 +5,7 @@ // Status: WIP #import "UIKit_Private.h" +#import "../OpenSwiftUIShims.h" #if __has_include() @@ -12,16 +13,47 @@ @implementation UIApplication (OpenSwiftUI_SPI) - (void)_performBlockAfterCATransactionCommits_openswiftui_safe_wrapper:(void (^)(void))block { - typedef void (*Func)(UIApplication *, SEL, void (^)(void)); - SEL selector = NSSelectorFromString(@"_performBlockAfterCATransactionCommits:"); - Func func = nil; - if ([self respondsToSelector:selector]) { - IMP impl = class_getMethodImplementation([self class], selector); - func = (Func)impl; - } - if (func != nil) { - func(self, selector, block); - } + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_performBlockAfterCATransactionCommits:", block(),void (^)(void)); + func(self, selector, block); +} + +- (void)startedTest_openswiftui_safe_wrapper:(NSString *)name { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"startedTest:", , NSString *); + func(self, selector, name); +} + +- (void)finishedTest_openswiftui_safe_wrapper:(NSString *)name { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"finishedTest:", , NSString *); + func(self, selector, name); +} + +- (void)failedTest_openswiftui_safe_wrapper:(NSString *)name withFailure:(NSError *)failure { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"failedTest:withFailure:", , NSString *, NSError *); + func(self, selector, name, failure); +} + +- (NSString *)_launchTestName_openswiftui_safe_wrapper { + OPENSWIFTUI_SAFE_WRAPPER_IMP(NSString *, @"_launchTestName", nil); + return func(self, selector); +} +@end + +@implementation UIView (OpenSwiftUI_SPI) +- (BOOL)_shouldAnimatePropertyWithKey_openswiftui_safe_wrapper:(NSString *)key { + OPENSWIFTUI_SAFE_WRAPPER_IMP(BOOL, @"_shouldAnimatePropertyWithKey:", NO, NSString *); + return func(self, selector, key); +} + +- (void)_setFocusInteractionEnabled_openswiftui_safe_wrapper:(BOOL)enabled { + OPENSWIFTUI_SAFE_WRAPPER_IMP(void, @"_setFocusInteractionEnabled:", , BOOL); + func(self, selector, enabled); +} +@end + +@implementation UIViewController (OpenSwiftUI_SPI) +- (BOOL)_canShowWhileLocked_openswiftui_safe_wrapper { + OPENSWIFTUI_SAFE_WRAPPER_IMP(BOOL, @"_canShowWhileLocked", NO); + return func(self, selector); } @end diff --git a/Tests/OpenSwiftUI_SPITests/Shims/AppKitPrivateTests.swift b/Tests/OpenSwiftUI_SPITests/Shims/AppKitPrivateTests.swift new file mode 100644 index 00000000..c6bfdc58 --- /dev/null +++ b/Tests/OpenSwiftUI_SPITests/Shims/AppKitPrivateTests.swift @@ -0,0 +1,20 @@ +// +// AppKitPrivateTests.swift +// OpenSwiftUI_SPITests + +import OpenSwiftUI_SPI +import Testing + +#if canImport(AppKit) +@MainActor +struct AppKitPrivateTests { + @Test + func application() { + let app = NSApplication.shared + let name = "ATest" + app.startedTest(name) + app.finishedTest(name) + app.failedTest(name, withFailure: nil) + } +} +#endif diff --git a/Tests/OpenSwiftUI_SPITests/Shims/CoreAnimationPrivateTests.swift b/Tests/OpenSwiftUI_SPITests/Shims/CoreAnimationPrivateTests.swift new file mode 100644 index 00000000..beda59a1 --- /dev/null +++ b/Tests/OpenSwiftUI_SPITests/Shims/CoreAnimationPrivateTests.swift @@ -0,0 +1,23 @@ +// +// CoreAnimationPrivateTests.swift +// OpenSwiftUI_SPITests + +import OpenSwiftUI_SPI +import Testing + +#if canImport(QuartzCore) +@MainActor +struct CoreAnimationPrivateTests { + @Test + func layer() { + let layer = CALayer() + #expect(layer.hasBeenCommitted == false) + layer.setAllowsEdgeAntialiasing(true) + layer.setAllowsGroupOpacity(true) + layer.setAllowsGroupBlending(true) + + layer.openSwiftUI_viewTestProperties = 42 + #expect(layer.openSwiftUI_viewTestProperties == 42) + } +} +#endif diff --git a/Tests/OpenSwiftUI_SPITests/Shims/UIKitPrivateTests.swift b/Tests/OpenSwiftUI_SPITests/Shims/UIKitPrivateTests.swift new file mode 100644 index 00000000..a33a123a --- /dev/null +++ b/Tests/OpenSwiftUI_SPITests/Shims/UIKitPrivateTests.swift @@ -0,0 +1,36 @@ +// +// UIKitPrivateTests.swift +// OpenSwiftUI_SPITests + +import OpenSwiftUI_SPI +import Testing + +#if canImport(UIKit) +@MainActor +struct UIKitPrivateTests { + @Test + func application() { + let app = UIApplication.shared + let name = "ATest" + app.startedTest(name) + app.finishedTest(name) + app.failedTest(name, withFailure: nil) + #expect(app._launchTestName() == nil) + } + + @Test + func view() { + let view = UIView() + #expect(view._shouldAnimateProperty(withKey: "frame") == false) + #expect(view._shouldAnimateProperty(withKey: "alpha") == false) + + view._setFocusInteractionEnabled(true) + } + + @Test + func viewController() { + let controller = UIViewController() + #expect(controller._canShowWhileLocked == true) + } +} +#endif