- Published on
> Ways to Shoot Yourself in the Foot with Method Swizzling
- Authors

- Name
- Mick MacCallum
- @0x7fs
Method swizzling grants immense power over Objective-C's runtime. With that power comes numerous opportunities to create bugs that are nearly impossible to track down. Let's explore the most common ways swizzling goes wrong.
Forgetting to Call the Original
The most basic mistake: your swizzled method doesn't call through to the original implementation.
// Broken - never calls original viewDidAppear:
- (void)tracking_viewDidAppear:(BOOL)animated {
[Analytics trackScreen:self];
// Oops, forgot to call original
}
Now every view controller in your app is broken. Subclass implementations of viewDidAppear: never run. Layouts might not complete. Data won't load. And the crash will happen somewhere completely unrelated to your swizzling code.
Swizzling in the Wrong Place
Swizzle too late and some code sees the un-swizzled version. Swizzle too early and dependencies might not be ready.
// Risky - may run after some view controllers already loaded
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[self setupSwizzling]; // Too late!
}
Always swizzle in +load. It runs before main(), guaranteeing consistent behavior from the first method call.
Double Swizzling
If your swizzling code runs twice, the implementations swap back to their original positions:
// Dangerous if called multiple times
- (void)setupSwizzling {
Method original = class_getInstanceMethod([UIView class], @selector(layoutSubviews));
Method swizzled = class_getInstanceMethod([UIView class], @selector(my_layoutSubviews));
method_exchangeImplementations(original, swizzled);
}
Call this twice and you've un-swizzled yourself. Always use dispatch_once:
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Swizzle here
});
}
Swizzling Without Checking Inheritance
Consider a class hierarchy where only the superclass implements a method. If you swizzle on the subclass, you're actually swizzling the superclass's method:
// ParentView implements -layoutSubviews
// ChildView does not override it
// This swizzles ParentView's implementation, affecting ALL ParentView subclasses
Method original = class_getInstanceMethod([ChildView class], @selector(layoutSubviews));
The safer pattern checks whether the method exists on the class itself:
SEL originalSel = @selector(viewDidAppear:);
SEL swizzledSel = @selector(tracking_viewDidAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSel);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSel);
BOOL didAdd = class_addMethod(class,
originalSel,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAdd) {
class_replaceMethod(class,
swizzledSel,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
Breaking Method Signatures
If your swizzled method has a different signature than the original, chaos ensues:
// Original takes (BOOL)animated
- (void)viewDidAppear:(BOOL)animated;
// Swizzled takes no arguments - WRONG
- (void)tracking_viewDidAppear {
[self tracking_viewDidAppear]; // Missing argument!
}
The compiler won't catch this because selector lookup is dynamic. At runtime, arguments get misaligned on the stack. You might get garbage values, crashes, or mysteriously wrong behavior.
Naming Collisions
What if two libraries both try to swizzle the same method with the same swizzled selector?
// Analytics SDK
- (void)analytics_viewDidAppear:(BOOL)animated { ... }
// Crash reporting SDK
- (void)analytics_viewDidAppear:(BOOL)animated { ... } // Collision!
One category replaces the other's method. Use distinctive prefixes that won't collide. Your company name or a UUID prefix works well.
Order Dependencies
Multiple swizzles on the same method create a chain. The order they execute matters:
// Library A swizzles in +load
// Library B swizzles in +load
// Final call chain: B's swizzle -> A's swizzle -> original
But +load order depends on link order and framework dependencies. Change your Podfile or add a new dependency, and suddenly the order changes. Behavior shifts mysteriously.
Threading Issues
If you swizzle while another thread is calling the method, you can hit a race condition:
// Thread 1: Executing viewDidAppear:
// Thread 2: Swizzling viewDidAppear: right now
// Thread 1 might call half-old, half-new implementation
This is why +load is important—it runs before any other code can use the methods.
Debugging Nightmares
When something goes wrong with swizzled code, debugging is painful. The stack trace shows your method name, but the implementation is different. You set a breakpoint in viewDidAppear:, but the swizzled code doesn't hit it.
The debugger's po command shows the original selector name, not the actual implementation running. Stepping through code jumps unexpectedly between implementations.
Add comments aggressively and consider logging:
- (void)tracking_viewDidAppear:(BOOL)animated {
#if DEBUG
NSLog(@"[SWIZZLE] tracking_viewDidAppear called on %@", self);
#endif
[self tracking_viewDidAppear:animated];
}
App Store Rejection Risks
While swizzling isn't explicitly prohibited, Apple has rejected apps for swizzling certain system methods. Swizzling private API methods is especially risky. If you're swizzling to work around App Store review or circumvent platform security, expect rejection.
When to Just Say No
If you can solve the problem another way, do that instead. Swizzling should be a last resort for:
- Adding behavior to closed-source code you can't modify
- Patching urgent bugs while waiting for a proper fix
- System-wide instrumentation during development
For code you control, prefer protocols, subclassing, delegation, and composition. They're predictable, debuggable, and won't mysteriously break when iOS updates.
Swizzling is a sharp tool. Respect its edges.
// Continue_Learning
Message Passing - The Heart of Objective-C
Unlike C++ virtual methods, Objective-C uses dynamic message passing. Understanding this mechanism unlocks the language's full power.
The Power of Method Swizzling in Objective-C
Method swizzling lets you replace method implementations at runtime. Here's how it works and when this powerful technique shines.
Operator Precedence Gotchas When Moving Between Swift and Objective-C
A refactoring mishap with arithmetic expressions led me down the rabbit hole of operator precedence differences between Swift and Objective-C. Here's what to watch for.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.