facebook / flipper

A desktop debugging platform for mobile developers.
https://fbflipper.com/
MIT License
13.33k stars 953 forks source link

iOS network observer has thoroughly broken swizzling #1674

Closed LeoNatan closed 3 years ago

LeoNatan commented 3 years ago

🐛 Bug Report

The iOS network observer has thoroughly broken swizzling; when it calls the original delegate method, it sends the incorrect selector, causing errors

Screen Shot 2020-11-11 at 22 25 23

_cmd here is incorrect. It should be URLSession:dataTask:didReceiveResponse:completionHandler:, but as you see, it's something else. This is broken behavior, and breaks code that use the well known pattern [proxyObj respondsToSelector:_cmd].

To Reproduce

Put a breakpoint in any NSURLSession delegate method. Print _cmd.

Environment

iOS 13.5 and iOS 14.2

LeoNatan commented 3 years ago

Ok, I will split the relevant code.

LeoNatan commented 3 years ago

I have moved the network to it's own class, so we are not confused with the app delegate. Here is a method description of the network delegate without flipper:

(lldb) po [self _methodDescription]
<LNNetworkDelegateAndInitiator(__detox_sync_URLSessionDelegateProxy): 0x600001128900>:
in LNNetworkDelegateAndInitiator(__detox_sync_URLSessionDelegateProxy):
    Instance Methods:
        - (void) __dtx_canaryInTheCoalMine___detox_sync_URLSessionDelegateProxy; (0x105b270a8)
        - (void) URLSession:(id)arg1 dataTask:(id)arg2 didReceiveResponse:(id)arg3 completionHandler:(^block)arg4; (0x101fbdb00)
        - (void) URLSession:(id)arg1 task:(id)arg2 didCompleteWithError:(id)arg3; (0x101fbdf10)
in LNNetworkDelegateAndInitiator:
    Properties:
        @property (readonly) unsigned long hash;
        @property (readonly) Class superclass;
        @property (readonly, copy) NSString* description;
        @property (readonly, copy) NSString* debugDescription;
    Instance Methods:
        - (id) init; (0x101fc02d0)
        - (void) .cxx_destruct; (0x101fc06f0)
        - (void) start; (0x101fc03d0)
        - (void) URLSession:(id)arg1 task:(id)arg2 didCompleteWithError:(id)arg3; (0x101fc0630)
        - (void) URLSession:(id)arg1 dataTask:(id)arg2 didReceiveResponse:(id)arg3 completionHandler:(^block)arg4; (0x101fc0470)
        - (void) URLSession:(id)arg1 dataTask:(id)arg2 didReceiveData:(id)arg3; (0x101fc0570)
in NSObject:
...

As you can see, the object implements - (void) URLSession:(id)arg1 dataTask:(id)arg2 didReceiveData:(id)arg3; (0x101fc0570). However, if I enable flipper, that method is no longer called.

Here is the new project: IsolatedSwizzles.zip

Check out DetoxSwizzling.m for the swizzles.

LeoNatan commented 3 years ago

As you can see from the method description, all the Detox swizzles do is to dynamically create the LNNetworkDelegateAndInitiator(__detox_sync_URLSessionDelegateProxy) class, which is a subclass of LNNetworkDelegateAndInitiator, and add three functions. The original class remains intact, no method of that class is actually swizzled, and yet, the didReceiveData: method is not called correctly with flipper.

LeoNatan commented 3 years ago

I suspect that flipper is failing to support dynamic classes, and thus is unable to figure out that the object responds to that method. Now that you have fixed _methodDescription on your end, try looking at what flipper/Flex sees.

priteshrnandgaonkar commented 3 years ago

@LeoNatan , I have updated the PR which fixes all the issue. You can try out the changes and confirm if it works. Copy paste the change in your xcworkspace. Let me know if it fixes.

LeoNatan commented 3 years ago

Guys, should I close the issue? Or you can, as it is now solved in the PR. Thanks