ReactiveX / RxSwift

Reactive Programming in Swift
MIT License
24.36k stars 4.16k forks source link

[Catalyst][RxCocoa] RxSearchBarDelegateProxy unrecognized selector #2161

Closed taher-mosbah closed 4 years ago

taher-mosbah commented 4 years ago

Short description of the issue:

RxSearchBarDelegateProxy not working on Catalyst.

2020-04-01 09:52:07.545777+0200 App[3676:38433] -[RxCocoa.RxSearchBarDelegateProxy searchBarTextDidBeginEditing:]: unrecognized selector sent to instance 0x6000026eb4e0
2020-04-01 09:52:07.546230+0200 App[3676:38433] [General] -[RxCocoa.RxSearchBarDelegateProxy searchBarTextDidBeginEditing:]: unrecognized selector sent to instance 0x6000026eb4e0
2020-04-01 09:52:07.557947+0200 App[3676:38433] [General] (
    0   CoreFoundation                      0x00007fff35f61d07 __exceptionPreprocess + 250
    1   libobjc.A.dylib                     0x00007fff6ec865bf objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff35fe0c97 -[NSObject(NSObject) __retain_OA] + 0
    3   CoreFoundation                      0x00007fff35ec657b ___forwarding___ + 1427
    4   CoreFoundation                      0x00007fff35ec5f58 _CF_forwarding_prep_0 + 120
    5   UIKitCore                           0x00007fff76bdf411 -[UISearchBar(UISearchBarStatic) _searchFieldBeginEditing] + 80
    6   UIKit                               0x00007fff7017142c -[UISearchBarAccessibility _searchFieldBeginEditing] + 46
    7   UIKitCore                           0x00007fff76a8e3df -[UIApplication sendAction:to:from:forEvent:] + 83
    8   UIKitCore                           0x00007fff76a96f01 -[UIControl sendAction:to:forEvent:] + 241
    9   UIKitCore                           0x00007fff76a94fb1 -[UIControl _sendActionsForEvents:withEvent:] + 410
    10  UIKitCore                           0x00007fff775071e9 -[UITextField _attachFieldEditor] + 668
    11  UIKitCore                           0x00007fff774fee46 -[UITextField _becomeFirstResponder] + 174
    12  UIKitCore                           0x00007fff76be1bb6 -[UISearchTextField _becomeFirstResponder] + 50
    13  UIKitCore                           0x00007fff774fed85 -[UITextField __resumeBecomeFirstResponder] + 27
    14  UIKitCore                           0x00007fff76be1e94 __42-[UISearchTextField _becomeFirstResponder]_block_invoke + 308
    15  UIKitCore                           0x00007fff76be1d5e -[UISearchTextField _becomeFirstResponder] + 474
    16  UIKitCore                           0x00007fff768bfd29 -[UIResponder becomeFirstResponder] + 703
    17  UIKit                               0x00007fff700e25d6 -[UITextInputUIResponderAccessibility becomeFirstResponder] + 44
    18  UIKitCore                           0x00007fff768f7116 -[UIView(Hierarchy) becomeFirstResponder] + 84
    19  UIKitCore                           0x00007fff774fdfbc -[UITextField becomeFirstResponder] + 188
    20  UIKitCore                           0x00007fff76bdfaa2 -[UISearchBar(UISearchBarStatic) becomeFirstResponder] + 48
    21  UIKitCore                           0x00007fff76a599da -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 44
    22  UIKitCore                           0x00007fff76a5985b _UIGestureRecognizerSendTargetActions + 109
    23  UIKitCore                           0x00007fff76a595aa _UIGestureRecognizerSendActions + 329
    24  UIKitCore                           0x00007fff76fa050b -[UIGestureRecognizer _updateGestureForActiveEvents] + 735
    25  UIKitCore                           0x00007fff7678ce6e _UIGestureEnvironmentUpdate + 2788
    26  UIKitCore                           0x00007fff76a52da8 -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 467
    27  UIKitCore                           0x00007fff76a5267c -[UIGestureEnvironment _updateForEvent:window:] + 200
    28  UIKitCore                           0x00007fff76a52226 -[UIWindow sendEvent:] + 4417
    29  UIKitCore                           0x00007fff76a5053f -[UIApplication sendEvent:] + 364
    30  UIKit                               0x00007fff70106941 -[UIApplicationAccessibility sendEvent:] + 85
    31  UIKitCore                           0x00007fff76a4e8f4 __dispatchPreprocessedEventFromEventQueue + 7416
    32  UIKitCore                           0x00007fff76922975 __handleEventQueueInternal + 6306
    33  UIKitCore                           0x00007fff76920aa1 __handleHIDEventFetcherDrain + 130
    34  CoreFoundation                      0x00007fff35ee5f12 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    35  CoreFoundation                      0x00007fff35ee5eb1 __CFRunLoopDoSource0 + 103
    36  CoreFoundation                      0x00007fff35ee5ccb __CFRunLoopDoSources0 + 209
    37  CoreFoundation                      0x00007fff35ee49fa __CFRunLoopRun + 927
    38  CoreFoundation                      0x00007fff35ee3ffe CFRunLoopRunSpecific + 462
    39  HIToolbox                           0x00007fff34b17abd RunCurrentEventLoopInMode + 292
    40  HIToolbox                           0x00007fff34b176f4 ReceiveNextEventCommon + 359
    41  HIToolbox                           0x00007fff34b17579 _BlockUntilNextEventMatchingListInModeWithFilter + 64
    42  AppKit                              0x00007fff33162c99 _DPSNextEvent + 883
    43  AppKit                              0x00007fff331614e0 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1352
    44  AppKit                              0x00007fff331531ee -[NSApplication run] + 658
    45  AppKit                              0x00007fff33124ff6 NSApplicationMain + 777
    46  AppKit                              0x00007fff33446a85 _NSApplicationMainWithInfoDictionary + 16
    47  UIKitMacHelper                      0x00007fff67090e00 UINSApplicationMain + 322
    48  UIKitCore                           0x00007fff767776ff UIApplicationMain + 2130
    49  App                                 0x000000010001ee44 main + 372
    50  libdyld.dylib                       0x00007fff6fe2dcc9 start + 1

Expected outcome: Delegate methods should work like on iOS as it is marked as available on Catalyst on Apple documentation.

What actually happens:

The app crashs with unrecognized selector sent to instance

RxSwift/RxCocoa/RxBlocking/RxTest version/commit

RxSwift 5.5.1

Platform/Environment

How easy is to reproduce? (chances of successful reproduce after running the self contained code)

Xcode version:

11.3.1

Installation method:

I have multiple versions of Xcode installed: (so we can know if this is a potential cause of your issue)

Level of RxSwift knowledge: (this is so we can understand your level of knowledge and formulate the response in an appropriate manner)

EricAppel commented 4 years ago

Here's a sample project using Swift Package Manager and RxSwift 5.1.1 that demonstrates the bug. We are not receiving values when subscribing to UISearchBar.rx.value and are experiencing a crash when providing our own delegate for text changes.

CatalystSearch.zip

freak4pc commented 4 years ago

@EricAppel Thanks for the repro. For now i'd use text instead of value. I'll see if I can debug this.

freak4pc commented 4 years ago

I actually just ran your project and it works fine for me. That's really strange:

image

dimitris-c commented 4 years ago

I had a look at this one and it seems to be within the _RXDelegateProxy when collecting void selectors (collectVoidSelectorsForProtocol). While running on iOS the selectors for UISearchBarDelegate were showing up but not when running on Mac Catalyst. And with no call to forwardInvocation on _RXDelegateProxy means sentMessage and methodInvoked will never be called.

I've also run a few other delegate methods like this UITabBarController().rx.delegate.methodInvoked(...).debug().subscribe() to trigger the initialisation of _RXDelegateProxy and it seems the UISearchBar is the only one that's not cooperating...

So maybe this is a Mac Catalyst issue?! not sure...

tsabend commented 4 years ago

@freak4pc did u run for catalyst? that screenshot looks like iOS and the bug only appears on catalyst.

taher-mosbah commented 4 years ago

@dimitris-c did you see any workarounds maybe ?

freak4pc commented 4 years ago

Sorry about the confusion. This definitely seems like an issue in Catalyst worth reporting, but I'm not sure where it stems from.

Even the raw version of this doesnt work:

searchBar.rx
    .delegate
    .methodInvoked(#selector(UISearchBarDelegate.searchBar(_:textDidChange:)))
freak4pc commented 4 years ago

Also, this is specific to UISearchBar, because even when trying other DelegateProxy-using constructs (like NSTextViewStorage, e.g. UITextView) it seems to work just fine even in Catalylst.

dimitris-c commented 4 years ago

@freak4pc this seems to be an issue with Catalyst, you can read my previous answer to see my findings.

tsabend commented 4 years ago

I'm happy to write or help promote a radar, but I'm not quite sure what the Catalyst issue is.

dimitris-c commented 4 years ago

Created a small snippet that can be used to see the issue. On Catalyst the objc runtime doesn't seem to pick up the search bar delegate method, changing the UISearchBarDelegate to another delegate prints its declared methods.

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface CatalystIssueOnUISearchBar : NSObject
@end

NS_ASSUME_NONNULL_END

CatalystIssueOnUISearchBar.m class

#import "CatalystIssueOnUISearchBar.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@interface Delegate : NSObject<UISearchBarDelegate>

@end

@implementation Delegate

@end

@implementation CatalystIssueOnUISearchBar

- (instancetype)init
{
    self = [super init];
    if (self) {
        Class targetClass = Delegate.class;
        unsigned int count;
        Protocol *__unsafe_unretained *protocols = class_copyProtocolList(targetClass, &count);

        for (unsigned int i = 0; i < count; i++) {

            unsigned int protocolMethodCount = 0;
            struct objc_method_description *pMethods = protocol_copyMethodDescriptionList(protocols[i], NO, YES, &protocolMethodCount);

            for (unsigned int i = 0; i < protocolMethodCount; ++i) {
                struct objc_method_description method = pMethods[i];

                NSLog(@"%@", NSStringFromSelector(method.name));
            }

            free(pMethods);

        }

        free(protocols);
    }
    return self;
}

@end
tsabend commented 4 years ago

Thanks for that @dimitris-c. I made a little swift project that replicates based on that snippet CatalystProtcolBug.zip

and filed a radar with apple: FB7710557

freak4pc commented 4 years ago

@tsabend thank you so much ! Would it be worth to also file on bugs.swift.org ?

freak4pc commented 4 years ago

I'm going to close this one or now. Even though it's a good reference, unfortunately there's nothing we can do to fix it without Apple's help.