Preface
Nearly all actual projects don’t require us to write every module from scratch; instead, we use third-party libraries to solve our issues, speed up development, and let us focus on our key features.
In order to use the features of third-party libraries, especially when importing them as modules through Cocoapod or SPM, you must import them everywhere.
Imagine, however, that after using Kingfisher for image loading for a while, you find that Nuke or SDWebImage performs much better for your needs.
If this is the case, you must replace the old module with the new one in all of your files. the situation will get even worse when you have to replace or remove all of the old module functions from your project because you are using so many of them.
In such a case, you can use the Proxy design pattern as a savior.
Definition
The proxy design pattern is a structural design pattern that we use to provide a placeholder for another object and control access to it.
To explain it better, first, take a look at the logical diagram of proxy:
As you can see, we have blocked all client access to the real object since they can only communicate with the proxy object. with this approach, we protect and hide all of the real object’s functions and variables from other project components.
Applicability
In general, there are three different types of proxy design patterns:
Remote proxy: Represents actual objects in a different space. for example, REST APIs create objects on the server, and clients use REST requests to access those objects.
Virtual proxy: Creates expensive objects on demand.in this approach, proxy objects stand as a substitute for the real object but act in a different way to delay its creation.
Protection proxy: controls access to the real object. the protection proxy is also useful when the real object needs various access roles for every client object or component.
Implementation
First, let me show the remote proxy in the code:
As you can see from the code, the real object was created on the server side and sent to the client after serialization. It wasn’t created in our app. The client obtained this object, deserialized it, and used it within the app. If we assume that the client only requests updates or receives the most recent value, then the user object is a remote proxy object, and the API class serves as our proxy to access the user object.
Next, let's look at the virtual proxy in the code:
As you can see from the code above, the placeholder image is returned if the getImage function is called before the image is prepared to display. Until the actual image process (downloading or drawing) is complete, a placeholder image is a virtual object that stands in for a real image. Following that, calling the getImage or getSize functions will result in a real image being returned.
Finally, we must specify how other objects can access our actual objects in order to implement a protection proxy.
As we discussed in the introduction to this article, let’s solve the image loader issue. This problem can be resolved by first using a protocol to specify how other objects can access our image loader.
As you see, all input and output of the loadImage function are Swift features or Apple’s first-party libraries for the iOS platform (like UIKit).
Now, we create another file and import the required third-party library inside it and implement ImageLoaderPorotocol.
You can even use this technique when using RxSwift or Combine. here is an example of the RxSwift version:
As you can see, the codes above use the Nuke image loader but instead return a regular image.
With this method, we don’t have to import Nuke into each and every file that needs to load an image, and you can swap out the Nuke library whenever you like with any other image loader by simply implementing the ImageLoaderProtocol function with a new image loader library (like KingFisher or SDWebImage).
FAQ
When should we use the proxy design pattern?
Proxy design patterns are typically used when we try to limit access to a specific object via API and hide it from other parts of a project (protection proxy). but as you can see in the example above, you can use proxy design patterns when you want to virtualize your real object with a placeholder to defer the real object’s cost of creation (virtual proxy) or use an object that is placed in another space rather than your code (remote proxy).
What’s different between the Proxy and Decorator design patterns? (Proxy vs Decorator)
While the Decorator design pattern wraps the real object and gives it more responsibility, the primary purpose of using a proxy design pattern is to hide and conceal the real object from clients.
What’s different between the Proxy and Adaptor design patterns? (Proxy vs Adaptor)
The adaptor design pattern is primarily used to adapt to incompatible and legacy codes. It wraps and provides a different interface for the real object.
For example, if your app uses multiple login methods (such as Google, Apple, Facebook, etc.), all of them must confirm the login protocol. By using this method, you can communicate with your login systems effectively and without a boilerplate.
What’s different between Proxy and facade design patterns? (Proxy vs facade)
The facade design pattern is primarily used to simplify the use of complex objects or libraries. While a proxy will wrap only one object to control access to it, a facade wraps one or more objects to simplify their complex behavior or creation.
In summary, the facade reduces communication and dependencies between objects, whereas the proxy serves as a bridge between the client and the wrapped object.
My Github
My Personal website
Top comments (0)