7

Swizzling in Swift 4 no longer works.

Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift

This is something I have found a solution to so wanted to leave the questions and answer for others.

Christopher Rex
  • 392
  • 3
  • 10

1 Answers1

8

initialize() is no longer exposed: Method 'initialize()' defines Objective-C class method 'initialize', which is not permitted by Swift

So the way to do it now is to run your swizzle code via a public static method.

e.g

In the extension: (This extension is used in the kickstarted open source code: https://github.com/kickstarter/ios-oss/blob/master/Library/DataSource/UIView-Extensions.swift)

private var hasSwizzled = false

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

        hasSwizzled = true
        swizzle(self) /* This is pseudo - run your method here */
    }
}

In the app delegate: (This method is used in the kickstarted open source code: https://github.com/kickstarter/ios-oss/blob/7c827770813e25cc7f79a28fa151cd713efe936f/Kickstarter-iOS/AppDelegate.swift#L33)

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: UIApplicationLaunchOptionsKey: Any]?) -> Bool 
{
    UIView.doBadSwizzleStuff()
}

Another way is to use a singleton:

extension UIView {
    static let shared : UIViewController = {
        $0.initialize()
        return $0
    }(UIViewController())

    func initialize() {
        // make sure this isn't a subclass
        guard self === UIViewController.self else { return }

        let swizzleClosure: () = {
            UIViewController().swizzle() /* This is pseudo - run your method here */
        }()
        swizzleClosure
    }
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: UIApplicationLaunchOptionsKey: Any]?) -> Bool 
{
    _  = UIViewController.shared
}
Christopher Rex
  • 392
  • 3
  • 10
  • 3
    this only works if you have access to the AppDelegate source code. I'm writing a library, and would like to make use of Swizzling for a specific method to be run in the ViewController's "viewDidLoad" block. I'm trying to make my library as easy as possible for customers to implement, and while I could ask them to add code to the AppDelegate, I would rather use Swizzling if possible (with the option for them to disable it if desired). Any ideas on this to get "AppDelegate-free" swizzling working in Swift 4.0? Thanks! Craig – CPR Oct 23 '17 at 10:08
  • 1
    I saw some code (that i don't remember the link too, if i find again will post). But they overrode UIResponder I think and then called the doBadSwizzle() method from there allowing them to have it work without access to AppDelegate. I'm not sure the exact implementation details anymore – Christopher Rex Oct 24 '17 at 11:12
  • 1
    I think you're referring to this post: http://jordansmith.io/handling-the-deprecation-of-initialize/. I'm having a discussion with Jordan on his blog about it, so feel free to join in there if you have anything to add. Thank you! – CPR Oct 24 '17 at 12:46
  • 1
    Just found that article again and yes it is that one. I was coming here to post the link for you. – Christopher Rex Oct 25 '17 at 15:39
  • @CPR I have just stumbled on this answer and the jordansmith.io post. There was a comment about this not working after 13.4 which I have verified as true. Does anybody have a solution to invoke the swizzling on app launch when you don't have access to the AppDelegate? – Ryan C. Payne Oct 21 '21 at 17:10
  • @RyanC.Payne The only way to do it that I've found is to start the swizzle using an ObjC category. The rest of the code can be in Swift, but there's no getting around using a little bit of ObjC to initiate the swizzle. – CPR Oct 25 '21 at 09:39