Swift 5のSwizzleについて1つのサンプルにまとめました。
自分もデバッグや一時的な動作変更に使う程度なので、間違いや補足などありましたらご指摘願います。
UIView+Swizzle.swift
import UIKit
extension UIView {
private struct SwizzleStatic {
static var once = true
}
class func swizzle() {
guard SwizzleStatic.once else {
return
}
SwizzleStatic.once = false
let swizzleMethod = { (original: Selector, swizzled: Selector) in
guard let originalMethod = class_getInstanceMethod(self, original),
let swizzledMethod = class_getInstanceMethod(self, swizzled) else {
return
}
method_exchangeImplementations(originalMethod, swizzledMethod)
}
let swizzleClassMethod = { (original: Selector, swizzled: Selector) in
guard let originalMethod = class_getClassMethod(self, original),
let swizzledMethod = class_getClassMethod(self, swizzled) else {
assertionFailure("The methods are not found!")
return
}
method_exchangeImplementations(originalMethod, swizzledMethod)
}
// イニシャライザ
swizzleMethod(#selector(UIView.init(frame:)),
#selector(UIView.swizzled_init(frame:)))
// クラスメソッド
swizzleClassMethod(#selector(UIView.animate(withDuration:delay:options:animations:completion:)),
#selector(UIView.swizzled_animate(withDuration:delay:options:animations:completion:)))
// インスタンスメソッド
swizzleMethod(#selector(UIView.addSubview(_:)),
#selector(UIView.swizzled_addSubview(_:)))
// プロパティ getter
swizzleMethod(#selector(getter: UIView.frame),
#selector(UIView.swizzled_get_frame))
// プロパティ setter
swizzleMethod(#selector(setter: UIView.frame),
#selector(UIView.swizzled_set_frame(_:)))
}
// イニシャライザを上書き
@objc func swizzled_init(frame: CGRect) -> UIView {
let instance = swizzled_init(frame: frame)
print("\(type(of: self)):\(#function):\(#line), \( instance )")
return instance
}
// クラスメソッドを上書き
@objc class func swizzled_animate(withDuration duration: TimeInterval, delay: TimeInterval, options: UIView.AnimationOptions = [], animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
print("\(type(of: self)):\(#function):\(#line), \( duration )")
swizzled_animate(withDuration: duration, delay: delay, options: options, animations: animations, completion: completion)
}
// インスタンスメソッドを上書き
@objc func swizzled_addSubview(_ view: UIView) {
print("\(type(of: self)):\(#function):\(#line), \( view )")
swizzled_addSubview(view)
}
// プロパティ getter を上書き
@objc func swizzled_get_frame() -> CGRect {
let frame = swizzled_get_frame()
print("\(type(of: self)):\(#function):\(#line), \( frame )")
return frame
}
// プロパティ setter を上書き
@objc func swizzled_set_frame(_ frame: CGRect) {
print("\(type(of: self)):\(#function):\(#line), \( frame )")
swizzled_set_frame(frame)
}
// 新しいプロパティを追加
private static var swizzleKeyName = "name"
var swizzledName: String? {
get {
objc_getAssociatedObject(self, &UIView.swizzleKeyName) as? String
}
set {
objc_setAssociatedObject(self, &UIView.swizzleKeyName, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
イニシャライザとクラスメソッドは一部のクラスで指定方法に違いがあるようです。
有効化
上記UIView+Swizzle.swift
をプロジェクト内に置き、AppDelegateのapplication(_:didFinishLaunchingWithOptions:)
で呼び出すだけです。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
UIView.swizzle()
...
return true
}