I did a quick experiment using two UICollectionView
methods as a guinea pig.
The first one:
// UICollectionViewCell's
@objc(_bridgedUpdateConfigurationUsingState:)
dynamic open func updateConfiguration(using state: UICellConfigurationState)
Note "dynamic" keyword, it is important and allows me to write:
extension UICollectionViewCell {
@_dynamicReplacement(for: updateConfiguration(using:))
func myUpdateConfiguration(using state: UICellConfigurationState) {
print("enter myUpdateConfiguration")
updateConfiguration(using: state)
print("exit myUpdateConfiguration")
}
}
and without doing anything else myUpdateConfiguration
will be called instead of the original method. Note that within it I call the original method via the original name. This is new in Swift 5.something "native swift swizzling".
Now to another dude:
// UICollectionViewCell's
open func dragStateDidChange(_ dragState: UICollectionViewCell.DragState)
If you try the same approach as above:
extension UICollectionViewCell {
// MARK: wrong. don't do that ❌
@_dynamicReplacement(for: dragStateDidChange(_:))
func wrong_dragStateDidChange(_ dragState: UICollectionViewCell.DragState) {
print("enter myDragStateDidChange")
dragStateDidChange(dragState)
print("exit myDragStateDidChange")
}
}
This won't work (won't be called). The reason is – the method is not mark as dynamic. Old Obj-C swizzling to the rescue:
extension UICollectionViewCell {
// MARK: ✅
@objc func correct_dragStateDidChange(_ dragState: UICollectionViewCell.DragState) {
print("enter myDragStateDidChange")
correct_dragStateDidChange(dragState)
print("exit myDragStateDidChange")
}
}
...
// call this somewhere once, early in the app lifecycle:
let original = class_getInstanceMethod(UICollectionViewCell.self, #selector(UICollectionViewCell.dragStateDidChange(_:)))!
let swizzled = class_getInstanceMethod(UICollectionViewCell.self, #selector(UICollectionViewCell.correct_dragStateDidChange(_:)))!
method_exchangeImplementations(original, swizzled)
Note that within the implementation I'm calling the original method via the new name: which looks like recursion but it is not. If you were to call the original method via the original name – you'll get the infinite recursion you are talking about.
Hope this helps.