Developers of third-party SDKs sometimes omit necessary methods from their proxy objects in iOS. They either don’t realize the methods are necessary, or they assume that since their SDK works on their end during testing, that it will work when incorporated into a mobile app that uses other third-party SDKs.

This couldn’t be further from the truth.

In this post, we will explain the four methods that all proxy objects need to implement in order to work well within Objective-C’s dynamic runtime. Even if your SDK doesn’t need to use every method, you must implement them so that your SDK will not break functionality in other SDKs that rely on them.

We wanted to write this post after helping our partners with multiple bugs caused by this very problem. It only takes a few minutes to implement these methods, and you’ll make your code more robust and less likely to cause problems when interacting with other SDKs in production.

A Simple Visual Of A Proxy

First, we’ll define some terms that we’ll be referring to throughout this article.

The caller is an entity that wants to access information from a real object in an application. For the examples here, it will be a third-party SDK like Embrace.

The proxy is a fake stand-in object that another entity provides to control access to a real object. This could be another SDK like Firebase.

The real object is an object running in the app. It contains methods that other parties want to access.

Caller, proxy, and real object

So the flow to keep in mind is a caller tries to access information from an object by going through a proxy.

The Four Methods Your Proxy Objects Need

They are:

  • forwardingTargetForSelector: on NSObject instances, it tells the caller who should receive this message.
  • respondsToSelector: on NSObject instances, it tells the caller whether or not this object (or its surrogates) can respond to a given selector.
  • methodSignatureForSelector: on NSObject instances, it tells the caller the exact method signature required by the target to successfuly respond to a message.
  • forwardInvocation: on NSObject instances, it performs the actual selector. If called, it is expected that the selector will be handled or an exception thrown.

Note that none of those are actually methods on NSProxy. NSProxy is the specific class written by Apple for proxying objects, and as such, developers use this term generically to apply to any class that wants to proxy other classes. It is not necessary that such classes be NSProxies, and in fact it is much more common for something that is not an NSProxy to act like an NSProxy.

This is the source of many problems in the SDK ecosystem. Developers want to do NSProxy-like things, but they think they don’t have to actually build a full NSProxy. The famous last words of every SDK developer might as well be, “I can get away with just respondsToSelector."

Why You Need All Four Methods

When you test your SDK, it probably seems like you only need 2 or 3 of these methods, but that is because you are not testing complex scenarios where 2 other SDKs proxy your delegate at the same time.  It is exactly those kinds of complex scenarios that require full implementations to work.

respondsToSelector is critical and is often the first call made. The proxy should respond "true" if either itself or the class it is proxying respond to the selector. If both respond, the answer is still “true.” Implementation of this method should be quick, and you should err on the side of false positives. After all, you can still return a null methodSignatureForSelector if you change your mind later.

methodSignatureForSelector is critical. This call is made to figure out how to build the actual NSInvocation. Most developers cannot create a methodSignatureForSelector object and should never have to. If you are the proxy, then you also own the “real” instance. You can call the instanceMethodSignatureForSelector method on the real instance, which will give you the method signature you return to the caller of methodSignatureForSelector.

forwardingTargetForSelector is less critical. Generally, the forwarding system should work with or without this call. The trick is that if you screw up the other 3, or if there is some edge case that you can't predict, it becomes necessary to get to the real object behind the proxy. And this is the only way to do that. Not providing it doesn't make your proxy stronger or better, it just makes your proxy harder to work with.

forwardInvocation is critical as this is the actual method call. If you did the other 3 right, this should 100% always succeed.

The Common Mistakes We See

We tend to see two main ways that mobile SDK developers ship bad proxy implementations:

  1. No forwardingTargetForSelector.

Developers omit this as it is theoretically optional and they don't want a "leaky" proxy, meaning they don't want anybody to even know that they are proxying at all. That's fine as long as you do the other 3 methods correctly. However, in one third-party SDK, we discovered the developers weren’t forwarding requests to the real object through the methodSignatureForSelector method. Instead, they were responding to these calls by calling the requested method on the proxy itself. Since the proxy did not have the method in question, the caller received a response of nil.

Proxy does not have method that caller wants to call on real object

This is a bug in their proxy’s implementation, but what makes it worse is that they also didn’t provide the forwardingTargetForSelector method. This means that other SDKs cannot work around their bug. There’s no way to go around the proxy to get to the real object.

We’re all just stuck waiting for them to fix their bug.

Proxy is missing forwardingTargetForSelector

2. No methodSignatureForSelector.

This is a common method to omit as developers assume that only they will ever work with their proxies, and since they already know everything about their proxies, they’d never need this. Unfortunately, that breaks when their SDK is in an app with other SDKs that also want to proxy. Now the implementor who didn't include methodSignatureForSelector can't be safely called by any other SDKs.

From the original developers’ point of view, that might seem good. They might be thinking, “Why would the app ever need another SDK? We’re the best, and they should remove those other SDKs and just use ours." From the app owner’s perspective, this anti-collaborative thinking is going to get your SDK pulled.

The following diagram illustrates how a proxy that implements methodSignatureForSelector correctly can allow a caller to access the method signature from a real object.

Proxy successfully forwards request to real object

Summing It All Up

In the end, it takes just a couple minutes to implement all four of the methods we’ve described. If done properly, your SDK can work well with all existing iOS components, all future iOS components, and all existing and future SDKs. Objective-C is a fully dynamic runtime, and these four methods are the key to that dynamism. Omitting any of them is not being clever, it’s breaking the system.

As SDK developers, our job is to add functionality to mobile apps without sacrificing the user experience. Properly implemented proxies are one way to allow multiple SDKs to play nicely. We hope you’ve found this post helpful, and that you feel empowered to snuff out any proxy shortcomings your SDK might have.

Who We Are

Embrace is a mobile monitoring and developer analytics platform. We are a one-stop shop for your mobile app’s performance and error debugging needs. If you’d like to learn more about Embrace, you can check out our website or visit our docs!