[SWIFT] Implement common processing using method swizzling on iOS

Introduction

――I thought about various patterns when implementing common processing on iOS, so I will summarize them. This time, let's focus on method swizzling.

environment

Xcode 12.3
Build version 12C33
Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
Target: x86_64-apple-darwin19.6.0

What is method swizzling?

--method swizzling is a function to replace the processing of existing methods. --Can be used for all Objective-C classes. --Can be implemented without inheriting common processing

Difference from inheritance in class

--I summarized the difference between inheritance in class and implementation in method swizzling.

⭕️
method swizzling The processing is reflected in the replaced class Since it is difficult to see the range of influence, it is difficult to deal with problems when they occur.
Class inheritance It is easy to control because the process is executed only by the inherited class. Because all the classes that need to be implemented need to be rewritten

――You can see that there are advantages and disadvantages to the implementation method. You need to think carefully about how to use it.

Usage example

kickstater --Used for processing that reflects ViewModel binding and style

Screen tracking

--If you replace the implementation of viewWillAppear and use Firebase etc., you can easily implement the process of sending screen display events.

import ObjectiveC
import UIKit

private func swizzle(_ vc: UIViewController.Type) {
  [
    (#selector(vc.viewWillAppear(_:)), #selector(vc.hoge_viewWillAppear(_:)))
  ]
  .forEach { original, swizzled in

    guard let originalMethod = class_getInstanceMethod(vc, original),
      let swizzledMethod = class_getInstanceMethod(vc, swizzled) else { return }

    let didAddViewDidLoadMethod = class_addMethod(
      vc,
      original,
      method_getImplementation(swizzledMethod),
      method_getTypeEncoding(swizzledMethod)
    )

    if didAddViewDidLoadMethod {
      class_replaceMethod(
        vc,
        swizzled,
        method_getImplementation(originalMethod),
        method_getTypeEncoding(originalMethod)
      )
    } else {
      method_exchangeImplementations(originalMethod, swizzledMethod)
    }
  }
}

private var hasSwizzled = false

extension UIViewController {
  public final class func doBadSwizzleStuff() {
    guard !hasSwizzled else { return }

    hasSwizzled = true
    swizzle(self)
  }

  @objc internal func hoge_viewWillAppear(_ animated: Bool) {
    self.hoge_viewWillAppear(animated)
    let instanceType = type(of: self)
    let name = String(reflecting: instanceType)
    print(name)
  }
}

Summary

--method swizzling is convenient, but you need to know how to use it and use it.

Reference link

Recommended Posts

Implement common processing using method swizzling on iOS
Implement declarative retry processing using Spring Retry
Using a local network on iOS 14