Closed joemasilotti closed 9 years ago
Hey Joe,
How approach did you take when swizzling the method? Are you familiar with the technique described here? https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/ (I realize that PCK itself doesn't generally do this properly!) I would be interested to see an isolated test case that can reproduce the issues you've encountered.
I am using PCK's +redirectSelector:to:andRenameItTo:
method to stub both adding and removing script handlers.
I've pulled out the relevant code into this gist. The spec helper tests pass but once the swizzling has occurred creating a WKWebView
crashes - as shown in WKWebViewSpec.mm
.
Thanks for the New Relic link, I'm going to convert my swizzling now and see if it makes a difference.
Thanks for the gist @joemasilotti , and for the informative link @briancroom
The intended test support for WKWebView would make a great contribution to PCK
Agreed! As soon as I have it working on Xcode 7 I will happily submit the PR.
I spent a few hours digging into this yesterday. I don't have a solution, but I feel like I might be getting towards something interesting. Or just digging myself deeper into a hole. I'll post my findings here to see if anyone has any insight.
Swizzling [WKUserContentController -addScriptMessageHandler:name:]
results in a crash. It does not matter if you swizzle via PCK's method or the "right" way as described in the linked New Relic article. There seems to be some internal validation going on and swizzling blows away the necessary set up.
Remembering that WebKit is open sourced, I was led to their unofficial GitHub repo. This led me digging into the actual code for WKUserContentController
and poking around. (Side note, how awesome is that? Code that's shipped with an iOS device and I'm seeing it? Very cool.)
Here we see that the proxy can be exposed via -_apiObject
. Calling this under test actually returns an instance of the WebUserContentControllerProxy
! Looking through that code we can see that the user script handlers are added to a hash map, m_scriptMessageHandlers
.
The proxy is actually a C++ class and the mentioned hash map is a private member. I haven't worked much in C++ but I'm pretty sure we can't access that variable like we could in Objective-C. If anyone has any insights I am open to trying anything.
It sounds like, if you really think you need to swizzle it you should call through to the original implementation after doing whatever it is you need to do.
Or, don't do this at all and treat the framework class as an external dependency. Wrap it, test that you call your wrapper, and move on :)
On Thu, Sep 3, 2015 at 8:11 AM, Joe Masilotti notifications@github.com wrote:
I spent a few hours digging into this yesterday. I don't have a solution, but I feel like I might be getting towards something interesting. Or just digging myself deeper into a hole. I'll post my findings here to see if anyone has any insight.
Swizzling [WKUserContentController -addScriptMessageHandler:name:] results in a crash. It does not matter if you swizzle via PCK's method or the "right" way as described in the linked New Relic article. There seems to be some internal validation going on and swizzling blows away the necessary set up.
Remembering that WebKit is open sourced, I was led to their unofficial GitHub repo https://github.com/WebKit/webkit. This led me digging into the actual code for WKUserContentController and poking around. (Side note, how awesome is that? Code that's shipped with an iOS device and I'm seeing it? Very cool.)
Here we see that the proxy can be exposed via -_apiObject https://github.com/WebKit/webkit/blob/master/Source/WebKit2/UIProcess/API/Cocoa/WKUserContentController.mm#L118. Calling this under test actually returns an instance of the WebUserContentControllerProxy! Looking through that code we can see that the user script handlers are added to a hash map, m_scriptMessageHandlers http://WebUserContentControllerProxy.
The proxy is actually a C++ class and the mentioned hash map is a private member https://github.com/WebKit/webkit/blob/a42f324713333d8fffca7461a5c22e685d85bb57/Source/WebKit2/UIProcess/UserContent/WebUserContentControllerProxy.h#L102. I haven't worked much in C++ but I'm pretty sure we can't access that variable like we could in Objective-C. If anyone has any insights I am open to trying anything.
— Reply to this email directly or view it on GitHub https://github.com/pivotal/PivotalCoreKit/issues/155#issuecomment-137480045 .
That's a good point. I ended up just subclassing WKUserContentController
and injecting it with Blindside. Oh well, at least I learned something!
Feel free to close this if you don't think it will be valuable to the project.
Edit: I couldn't call through to the original implementation because the crash occurs before the swizzled implementation even gets called.
This continues the discussion from an open Cedar issue related to Xcode 7.
When working with
WKWebView
you can create a JavaScript "bridge" via theWKUserContentController
. Via-addScriptMessageHandler:
you can create a hook that gets calls when special JavaScript methods are fired. Unfortunately, there is no way to read back these handlers once they are set, essentially making the property "write-only." (Yay, Apple!)To get around this I swizzled the method and exposed the parameters in my own dictionary. Testing with Cedar under Xcode 6 on iOS 8.X this works great, no issues. However, when updating to Xcode 7 and iOS 9 the swizzling creates all sorts of issues for
WKWebView
.So, I ask the community, how can one expose these "write-only" properties without swizzling?