Method swizzling in iOS swift
What is method swizzling ?? — this was a question asked in an interview. I didn’t knew the answer back then. I googled it and found many articles about method swizzling. But, I couldn’t understand the proper use case of method swizzling. Michael Mavris’s article on method swizzling was simple and easy to understand. Read it here.
--
Today, I was integrating FCM in my iOS app. I saw the same word method swizzling again in the firebase documentation. It said:
So, its an important thing to know. Let’s understand what it is.
What is method swizzling?
Method swizzling is the process of changing the implementation of an existing selector at runtime. Simply speaking, we can change the functionality of a method at runtime.
Note: This is an Objective-C runtime feature.
For example: If you want to track the keys and values added to the UserDefaults
and add a prefix string before all the keys, you can switch the implementation of the setValue:forKey
method with your own method. So all the calls made to the setValue:forKey
method will be routed to the new selector method. You can create the new key string with the prefix added and call the original implementation of setValue:forKey
method with the new key. You will be confused whether it will end up in an infinite loop or not. But it will not. (Note: You can do the same by using a category in Objc or extension in swift. Just create a new method and call the super setValue:forKey
method from this new method.But what if the imported third party libraries and frameworks use the setValue:forKey
method directly. These libraries are not aware of the new custom method as well as our requirement of adding the prefix to the keys. This is when method swizzling comes to play). I took this example from the following article 👇🏻👇🏻👇🏻👇🏻👇🏻👇🏻.
Do read Michael Mavris’s article here before proceeding. Have a look at the stackoverflow post on how to do method swizzling in swift 3 and 4.
How to do method swizzling in swift 4?
Well, its easy:
- Create a new method with your custom implementation for a method that you want to swizzle.
- Get the class representation.
- Get the old method selector reference.
- Get the new method selector reference.
- Ask the objective-C runtime to switch the selectors.
- Take a deep breath and relax.!! 😂.
Let’s swizzle!!
Let’s create a swizzled method for description
method of UIColor
class.
If you try to print a UIColor
object ,it would print the RGBA value like this:
print(UIColor.red) // prints UIExtendedSRGBColorSpace 1 0 0 1
We can print the colour because UIColor
has a description method which will return the string representation of the colour. Let’s try to swizzle this method.
import Foundationimport UIKitpublic extension UIColor {@objc func colorDescription() -> String {return "Printing rainbow colours."}private static let swizzleDesriptionImplementation: Void = {let instance: UIColor = UIColor.redlet aClass: AnyClass! = object_getClass(instance)let originalMethod = class_getInstanceMethod(aClass, #selector(description))let swizzledMethod = class_getInstanceMethod(aClass, #selector(colorDescription))if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
// switch implementation..
method_exchangeImplementations(originalMethod, swizzledMethod)}}()public static func swizzleDesription() {_ = self.swizzleDesriptionImplementation}}
The above code is pretty much self explanatory I believe. Now in my ViewController, I added the following code.
override func viewDidLoad() {super.viewDidLoad()print(UIColor.red)print(UIColor.green)UIColor.swizzleDesription()print(“\nswizzled\n”)print(UIColor.red)print(UIColor.red)UIColor.swizzleDesription()print(“\nTrying to swizzle again\n”)print(UIColor.red)print(UIColor.red)}
Output:
UIExtendedSRGBColorSpace 1 0 0 1UIExtendedSRGBColorSpace 0 1 0 1swizzledPrinting rainbow colours.Printing rainbow colours.Trying to swizzle againPrinting rainbow colours.Printing rainbow colours.
static
members of Swift are implicitly lazy
. That is the reason why swizzleDesriptionImplementation
is not called again and swizzling was not happened for the second time. Enclosing all the operations that modify the methods in the lazy initialization block of a computed global constant ensures that the procedure will be performed only once (since initialization of these variables or constants uses dispatch_once behind the scenes).
Drawbacks of method swizzling
- If you are using swizzling any standard class methods, then better make sure that the swizzling is successful and your new swizzled method is getting called. If you are using some frameworks (ex: Firebase) which uses swizzling ,make sure that you are not swizzling some method that they already swizzled . If swizzling happens multiple times , either your code won’t work ,or the firebase (or any other framework swizzling the same method) won’t work.
- When newer iOS versions are released, there are chances that the swizzling fails. You may have to cross check this every time.
- If you are shipping a framework (ex: some analytics framework) which is used by hundreds of apps, better not to use swizzling in this case. In case if you want to use swizzling, please make sure that the app developer is aware of the swizzling that the framework does. Make sure that it is added to the documentation.
- Swizzling inside a subclass will be a headace. unexpected things might happen.
References: nshipster , uraimo, medium.
Enjoy!!
If you enjoyed reading this post, please share and give some clapps so others can find it 👏👏👏👏👏 !!!!
You can follow me on Medium for fresh articles. Also, connect with me on LinkedIn.
If you have any comment, question, or recommendation, feel free to post them in the comment section below!