From 452be3534838b25d13db97d883f824aa3483a5d3 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Mon, 21 Sep 2026 09:31:14 -0400 Subject: [PATCH] refactor: share one UIHingeInteraction factory across the iOS module and view The TurboModule and the Fabric observer view each carried their own SDK compile guard, iOS 27.1 availability check, UIHingeInteraction construction and UIHinge status mapping. A new HingesMakeInteraction helper owns all of that and hands each call site the same payload shape, matching how Android shares HingeSource. Behavior is unchanged. --- ios/HingeInteraction.h | 10 ++++++++++ ios/HingeInteraction.mm | 26 ++++++++++++++++++++++++++ ios/HingesModule.mm | 37 ++++++++++++------------------------- ios/HingesObserverView.mm | 38 +++++++++++++++----------------------- 4 files changed, 63 insertions(+), 48 deletions(-) create mode 100644 ios/HingeInteraction.h create mode 100644 ios/HingeInteraction.mm diff --git a/ios/HingeInteraction.h b/ios/HingeInteraction.h new file mode 100644 index 0000000..f937155 --- /dev/null +++ b/ios/HingeInteraction.h @@ -0,0 +1,10 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^HingesUpdateHandler)(NSArray *hinges); + +/// Returns nil when the SDK or runtime has no UIHingeInteraction. +id _Nullable HingesMakeInteraction(HingesUpdateHandler handler); + +NS_ASSUME_NONNULL_END diff --git a/ios/HingeInteraction.mm b/ios/HingeInteraction.mm new file mode 100644 index 0000000..3271471 --- /dev/null +++ b/ios/HingeInteraction.mm @@ -0,0 +1,26 @@ +#import "HingeInteraction.h" + +id HingesMakeInteraction(HingesUpdateHandler handler) +{ +#if defined(__IPHONE_27_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_27_1 + if (@available(iOS 27.1, *)) { + return [[UIHingeInteraction alloc] + initWithUpdateHandler:^(UIHingeInteraction *interaction, UIHingeInteractionUpdate *update) { + UIHinge *hinge = update.hinge; + if (hinge == nil) { + handler(@[]); + return; + } + NSString *status = @"unknown"; + switch (hinge.status) { + case UIHingeStatusClosed: status = @"closed"; break; + case UIHingeStatusPartiallyOpen: status = @"partiallyOpen"; break; + case UIHingeStatusFullyOpen: status = @"fullyOpen"; break; + case UIHingeStatusUnknown: break; + } + handler(@[@{@"status": status, @"angle": @(hinge.angle), @"hasAngle": @YES}]); + }]; + } +#endif + return nil; +} diff --git a/ios/HingesModule.mm b/ios/HingesModule.mm index 3a6edbe..cdb74c1 100644 --- a/ios/HingesModule.mm +++ b/ios/HingesModule.mm @@ -1,5 +1,7 @@ #import "HingesModule.h" +#import "HingeInteraction.h" + #import #import #import @@ -88,35 +90,20 @@ - (void)startObserving:(double)rootTag if (observation.interaction == nil) { UIView *view = [self->_surfacePresenter surfaceForRootTag:tag.integerValue].view; observation.view = view; -#if defined(__IPHONE_27_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_27_1 - if (@available(iOS 27.1, *)) { - if (view != nil) { - __weak HingesModule *weakSelf = self; - __weak HingeRootObservation *weakObservation = observation; - UIHingeInteraction *interaction = [[UIHingeInteraction alloc] - initWithUpdateHandler:^(UIHingeInteraction *interaction, UIHingeInteractionUpdate *update) { - HingesModule *strongSelf = weakSelf; - HingeRootObservation *strongObservation = weakObservation; - if (strongSelf == nil || strongObservation == nil) return; - UIHinge *hinge = update.hinge; - NSArray *hinges = @[]; - if (hinge != nil) { - NSString *status = @"unknown"; - switch (hinge.status) { - case UIHingeStatusClosed: status = @"closed"; break; - case UIHingeStatusPartiallyOpen: status = @"partiallyOpen"; break; - case UIHingeStatusFullyOpen: status = @"fullyOpen"; break; - case UIHingeStatusUnknown: break; - } - hinges = @[@{@"status": status, @"angle": @(hinge.angle), @"hasAngle": @YES}]; - } - [strongSelf updateRoot:tag observation:strongObservation hinges:hinges]; - }]; + if (view != nil) { + __weak HingesModule *weakSelf = self; + __weak HingeRootObservation *weakObservation = observation; + id interaction = HingesMakeInteraction(^(NSArray *hinges) { + HingesModule *strongSelf = weakSelf; + HingeRootObservation *strongObservation = weakObservation; + if (strongSelf == nil || strongObservation == nil) return; + [strongSelf updateRoot:tag observation:strongObservation hinges:hinges]; + }); + if (interaction != nil) { observation.interaction = interaction; [view addInteraction:interaction]; } } -#endif } [self emitSnapshotForRoot:tag hinges:[self getSnapshot:rootTag][@"hinges"]]; }); diff --git a/ios/HingesObserverView.mm b/ios/HingesObserverView.mm index c8386a0..82649d2 100644 --- a/ios/HingesObserverView.mm +++ b/ios/HingesObserverView.mm @@ -1,5 +1,7 @@ #import "HingesObserverView.h" +#import "HingeInteraction.h" + #import #import #import @@ -26,31 +28,21 @@ - (instancetype)initWithFrame:(CGRect)frame if (self = [super initWithFrame:frame]) { static const auto defaultProps = std::make_shared(); _props = defaultProps; -#if defined(__IPHONE_27_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_27_1 - if (@available(iOS 27.1, *)) { - __weak HingesObserverView *weakSelf = self; - UIHingeInteraction *interaction = [[UIHingeInteraction alloc] - initWithUpdateHandler:^(UIHingeInteraction *interaction, UIHingeInteractionUpdate *update) { - HingesObserverView *strongSelf = weakSelf; - if (strongSelf == nil) return; - HingesObserverViewEventEmitter::OnHingesChange snapshot; - UIHinge *hinge = update.hinge; - if (hinge != nil) { - std::string status = "unknown"; - switch (hinge.status) { - case UIHingeStatusClosed: status = "closed"; break; - case UIHingeStatusPartiallyOpen: status = "partiallyOpen"; break; - case UIHingeStatusFullyOpen: status = "fullyOpen"; break; - case UIHingeStatusUnknown: break; - } - snapshot.hinges.push_back({status, hinge.angle, true}); - } - strongSelf->_snapshot = std::move(snapshot); - [strongSelf emitSnapshot]; - }]; + __weak HingesObserverView *weakSelf = self; + id interaction = HingesMakeInteraction(^(NSArray *hinges) { + HingesObserverView *strongSelf = weakSelf; + if (strongSelf == nil) return; + HingesObserverViewEventEmitter::OnHingesChange snapshot; + for (NSDictionary *hinge in hinges) { + snapshot.hinges.push_back( + {[hinge[@"status"] UTF8String], [hinge[@"angle"] doubleValue], [hinge[@"hasAngle"] boolValue]}); + } + strongSelf->_snapshot = std::move(snapshot); + [strongSelf emitSnapshot]; + }); + if (interaction != nil) { [self addInteraction:interaction]; } -#endif } return self; }