Why is method swizzling considered a footgun, and what do you do instead?

4.6.2 · L5 · Interview

Predict first

Inside a swizzled method you call self.swizzled_viewDidLoad() to run the original. Why does that not loop forever?

// A: the runtime special-cases the call
// B: after the exchange that selector points at the original implementation
// C: it does loop, you must guard with a flag
  • A
  • B
  • C

Predict first

Which is the safest way to observe when a view controller appears?

// A: swizzle viewDidAppear in +load
// B: use a supported hook, such as a lifecycle callback or a container that owns the controller
// C: replace the method with class_replaceMethod
  • A
  • B
  • C

Mental model. Rewiring the building’s switchboard so every call to “Reception” also rings your desk. It works until someone else rewires it too, or the building changes its numbers.

Swizzling changes behaviour process-wide and invisibly. Every caller of the selector, including system frameworks you do not control, now runs your code. It collides with other libraries that swizzle the same method (the order decides whether both run, and a wrong order silently drops one), and with a new OS release that renames, removes or reimplements the method. The call’s _cmd argument changes, which breaks implementations that inspect it. It is hard to debug, because the stack trace shows a method that does not appear in the source, and it is not type-checked, so a signature mismatch crashes at runtime.

If you must, follow rules: swizzle once, early, with exchange rather than replace, always call the original, give the swizzled method a unique prefix, and keep it out of release builds when it exists only for debugging or tests. Prefer a supported hook: a subclass, a delegate or lifecycle callback, a notification, a protocol the framework already offers, a proxy, or os_signpost/metrics for observation. Libraries that swizzle (analytics SDKs) deserve the most suspicion in a security or stability review.

The exchange swapped the two table entries, so the new name now leads to the old code. Calling it is how you reach the original.

A supported hook cannot collide with other code or break on an OS update. Swizzling should be the last resort.

Common traps

  • Swizzling in viewDidLoad or any code that can run twice. The second exchange reverts the first.
  • Shipping a swizzle for logging. It runs for every user and can break on the next OS release.

They might follow up with

  • How do you tell if a third-party SDK swizzles? Search its binary for method_exchangeImplementations and class_replaceMethod, or read its documentation.
  • What is a safer observation tool? A notification, a delegate method, or the framework’s own instrumentation.

Related questions

  • 4.6.1 How does method swizzling work, and why does it need dynamic in Swift?
  • 4.2.1 What is an Objective-C category, and how does it differ from a Swift extension?

← How does method swizzling work, and why does it need dynamic in Swift?