Swizzling causing recursion

424 Views Asked by At

So when I tried to swizzle UIImage's init(named:) so that I could set the accessibility identifier with the image's name, it seems like, even though I am calling method_exchangeImplementation, both my swizzled method ftg_imageNamed(named name: String) and init(named:) call my swizzled method: ftg_imageNamed(named name: String) creating an infinite loop. Why is that?

Calling method_exchangeImplementation

extension UIImage {

    static func swizzleInitImplementation() {
        let originalSelector =  #selector(UIImage.init(named:))
        let swizzledSelector = #selector(UIImage.ftg_imageNamed(named:))


        let imgSelf: AnyClass = self.classForCoder()

        guard  let originalMethod = class_getClassMethod(imgSelf, originalSelector),
            let swizzledMethod = class_getClassMethod(imgSelf, swizzledSelector) else {
                assertionFailure("The methodsw are not found")
                return
        }

        method_exchangeImplementations(originalMethod, swizzledMethod)
    }

    @objc static func ftg_imageNamed(named name: String)  {
        setAccessibilityLabel(name)
        self.ftg_imageNamed(named: name)
    }

}

The manual implementation which failed the same way.

extension UIImage {

    static func swizzleInitImplementation() {
        let originalSelector =  #selector(UIImage.init(named:))
        let swizzledSelector = #selector(UIImage.ftg_imageNamed(named:))


        let imgSelf: AnyClass = self.classForCoder()

        guard  let originalMethod = class_getClassMethod(imgSelf, originalSelector),
            let swizzledMethod = class_getClassMethod(imgSelf, swizzledSelector) else {
                assertionFailure("The methodsw are not found")
                return
        }

        let imp1 = method_getImplementation(originalMethod)
        let imp2 = method_getImplementation(swizzledMethod)
        method_setImplementation(originalMethod, imp2)
        method_setImplementation(swizzledMethod, imp1)

    }

    @objc static func ftg_imageNamed(named name: String)  {
        setAccessibilityLabel(name)
        self.ftg_imageNamed(named: name)
    }

}
1

There are 1 best solutions below

8
x4h1d On

Your swizzling seems "ok" until self.UI(named: name). So check that method for infinite loop problem.

Now, this is a bad implementation of swizzling. UIImage.init(named:) returns an instance of UIImage, where swizzled method UIImage.ftg_imageNamed(named:) returns Void. Both original and swizzled method should have the same params and return type, the implementation may vary.

You should consider a simple extension method to achieve what you want instead of swizzling.

Edit

extension UIImage {
    static func initIncludingAccessibility(named: String) -> UIImage {
       let img = UIImage(named: named)
       img.setAccessibilityLabel(named)
       return img
    }
}

use it as

let image = UIImage.initIncludingAccessibility(named: /* your_image_name*/)