A Practise of Method Swizzling in iOS Development
As many iOS developers know the Method Swizzling
is a ‘black magic’ technique of the Objective-C Runtime, and perhaps it is the most contentious of runtime hackery techniques.
Many people don’t like using the Method Swizzling
because they think it changes the default function and may impact a class that you don’t want to change. On the contrary, supporters think it is a good feature because of its ability to do complex tasks conveniently.
You can see how I came to using the Method Swizzling
and my practise and experience with it for some UI elements in my latest side iOS project, an app that focuses on all things comedy: especially jokes.
Demand
I needed to set a unified background color for all of views in the project. At the beginning, I wrote the following code in every view controller’s’ viewDidLoad
method:
// setup global background color
self.view.backgroundColor = WXGGlobalBackgroundColor;
Analysis
In this project, there were tons of views and controllers, so I found myself writing this code again and again. It was stupid and inefficient! I knew I needed to find a better solution for this. So, I began to think about Inheritance
and Method Swizzling
.
Why didn’t I use Inheritance
?
-
As we know, the UIKit framework has its own inherited hierarchy and Objective-C doesn’t support
Multiple Inheritance
, so if you want to set all views in the project, you cannot just create a subclass ofUIViewController
, maybe you need do more onUITableViewController
andUINavigationController
which is already a subclass ofUIViewController
. The result is that you increase the complexity of the inheritance structure and get no additional benefits. -
There are a lot of challenges in making sure everyone on a project knows how to use the custom subclass, because you need to provide documentation for each person on the project and must ensure the documentation is understood and clear when new team members enter the project.
Why did Method Swizzling
work better for me?
With Method Swizzling
you can change the default implement of UIViewController’s viewDidLoad method, all views in the project will change whatever it belongs to a UINavigationController
or a UITableViewController
. It is amazing! You just need to change one place and the whole project will be changed.
So, I began my experiment with Method Swizzling
.
Program
To solve the background color issue, I found an easy way to implement Method Swizzling
. You can check out the code below.
// UIViewController+Extension.h
#import <UIKit/UIKit.h>
@interface UIViewController (Extension)
@end
// UIViewController+Extension.m
#import "UIViewController+Extension.h"
#import <objc/runtime.h>
@implementation UIViewController (Extension)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(swizzled_viewDidLoad));
BOOL didAddMethod = class_addMethod([self class], @selector(viewDidLoad), method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod([self class], @selector(swizzled_viewDidLoad), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)swizzled_viewDidLoad {
// WXGLog(@"%@ loaded", self);
if (![self isKindOfClass:NSClassFromString(@"UIInputWindowController")]) {
self.view.backgroundColor = WXGGlobalBackgroundColor;
}
[self swizzled_viewDidLoad];
}
@end
I tried to keep the above code friendly for beginners of Method Swizzling
, and if needed, you can find some great resources to help you here.
Problem
After I finished implementing my solution using the Method Swizzling
, I began testing my code. Success, it worked like magic. I saw the right background color. A few days later, I wanted to add a textfield and a pop up keyboard, but this is where I found a bug. The whole screen was filled with the background and all the views disappeared.
When I debugged the view hierarchy, I saw the views there. Additionally, there was another view named UIInputSetContainerView
in front of all my views, it blocked the other views.
I added a line of debug code in the swizzled method, to try and find what the problem was. Then, I saw that a controller named UIInputWindowController
was called last. The issue was a private API managed by Apple. Developers are unable to use this API, despite this it was impacting the Method Swizzling
so my solution was to remove the private controller in the swizzled method. After that, no problems occurred again in my project.
Conclusion
Method Swizzling
is a dynamic feature that can exchange the implementations of two methods in runtime. This means it can implement some complex functions conveniently, however, it can also be dangerous to use because its scope can change the class of the project and can cause fatal errors for the project if done wrong.
Here are some pitfalls of Method Swizzling
. Pay attention to these when you use Method Swizzling
in your projects.
- Method swizzling is not atomic
- Changes behavior of un-owned code
- Possible naming conflicts
- Swizzling changes the method’s arguments
- The order of swizzles matters
- Difficult to understand (looks recursive)
- Difficult to debug
We can’t give up eating for fear of choking, so the best choice is to keep trying with the Method Swizzling
until you understand what’s going on and know how to fix it when it has bugs. Don’t give up. Then you will find that it’s a very useful technique for you to develop iOS apps.
Bonus Tip for UILabel
Change the default display of UILabel from “” to “-“ is as same as this page, so you can do it yourself if you are interested.
Read More about Method Swizzling
If you want to use Method Swizzling
with Swift, you can get help from here or here
Have fun!