When anyone mentions swizzling I automatically get worried. Method swizzling is an Objective C runtime feature which allows for the switching of method implementations. This means any function can theoretically be switch out for another one at run time. The Objective C runtime as a whole host of runtime features to modify classes, methods, objects, properties etc. We’re going to look at method swizzling, which as mentioned before, is the process of changing a function implementation for another one at runtime.
So is Swizzling really a bad thing?
If not done correctly swizzling can open a whole host of issues.
Harder to debug – As we are swapping method implementations out, this can make debugging slightly harder. If you pick up a new project and don’t know a certain method has been swizzled, it can be quite hard to follow the implementation addresses in the backtrace.
Anyone can swizzle – As a long as the class is an Objective C type its method can be swizzled. Anyone is free to change the code, as long as they know the class, and the name of the selector. This means any framework could modify parts of your app without you even knowing. It also means if you are swizzling a function, and another framework is swizzling the same function, there is going to be a mismatch, and only one implementation will be used.
Confusing – For anyone used to programming with swift and using the swift APIs, the Objective C runtime can be quite confusing, and hard to read. Having to get methods and implementations to swap around is not something that is done very often.
When to use Swizzling
The majority of the time I would say that swizzling should not be your first option, and that your problem could most likely be solved another way. However there is one use case I’ve come across which I feel is a good candidate for method swizzling.
App Delegate Swizzling for Framworks
Frameworks, be it 3rd party or local frameworks, have no knowledge of the app lifecycle. They do not get application events such as applicationWillEnterForeground, didFinishLaunchingWithOptions, handleEventsForBackgroundURLSession, etc. Frameworks do have access to the notification centre where they can subscribe to, and listen to changes to many of the application functions. However notifications are not sent with all of them. For example push notification configuration, and background fetching, do not send notifications when their respective functions have been called. So what happens when a framework needs to modify an app delegate function, but has no way to see the function.
Manually pass values in
The user could manually pass the values into the framework when the relevant app delegate method is fired. The example below shows an app delegate, and a framework class which would live inside a completely separate module.
Although this way of engineering does solve the problem, it does mean that everyone who adopts the framework will have to manually pass these values through.
It would be better if the user didn’t have to do this extra step, and the framework just handled the logic on its own. This is where method swizzling comes in. By swizzling the app delegates handleEventsForBackgroundURLSession function, the framework can take care of this logic without the user having to do anything.
The framework will now swizzle the implementation of handleEventsForBackgroundURLSession from the app delegate to an implementation of a method from inside the framework.
A few thing to note here.
First, it creates the selectors by getting a reference to the app delegate, and newly created function within the Framework. It then checks the app delegate to see if handleEventsForBackgroundURLSession: is already visible. If the user has already implemented handleEventsForBackgroundURLSession: then it has an implementation to swap, otherwise it will add the method to the app delegate using class_addMethod. Lets take a look at the two key swizzling functions in more detail.
This switches the implementation of two Method types. These Method types are pulled from the selectors created at the beginning, along with their respective class types.
class_addMethod(type(of: delegate), selector, method_getImplementation(swizzled), nil)
If the method implementation does not exist within the app delegate, then it will have nothing to swizzle. So in this instance a new method is added to the app delegate, using the function above. It creates a new method from a selector and the implementation of handleEventsForBackgroundURLSession: which lives inside the framework.
Now whenever handleEventsForBackgroundURLSession: is called in the app delegate, it will always call the function inside the framework. This is all well and good but what happens when the user still wants to provide their own functionally, or another framework wants to access handleEventsForBackgroundURLSession.
For this case I would always provide a way for the user to disable swizzling. This way they can manually go back to importing the values without swizzling. This seems to be the adopted way of handling swizzling as is used in many 3rd party frameworks, Firebase, Airship etc.
- Cheshire Half Marathon 2020 September 27, 2020With most of 2020 being a complete right off, it meant I could knuckle down and get plenty of good training in. Races should have been on the cards with Manchester marathon, Holkham triathlon and Valencia marathon being the main goals. However, like most events...
- Helvellyn September 13, 2020Hannah and I were due to get married on the 29th August and were supposed to be spending two weeks hiking around Canada. Then the coronavirus happened and all our plans went out the window. Instead of sitting home doing nothing, we decided to take...
- Uploading content, React, GraphQL, Rails and Active Storage April 26, 2020There are some great blog posts out there on all these topics but I found a lot to be out of date. The versions I’m using are below. Rails 6.0 React 16.3.1 Apollo 3.0.0 Rails To start off with we will set up our backend...
- Full-stack introduction using Rails, React, Docker and Heroku February 9, 2020Coming from mainly a mobile background, I’ve always been interested in learning new technologies that I wouldn’t use in my day to day field. I find it fascinating that especially with today’s technology it is easier for an individual to write an entire end to...
- Malaga Marathon 2019 December 21, 2019All the training these last few months had been leading up to one goal – to run a sub-3-hour marathon in the Malaga marathon. The training had been going great, and with a 1:22 in the Tatton Half, I felt like I had a great...
- Run Tatton Half Marathon 2019 November 9, 2019It’s been a while since my last run event, with the Chester half being back in May. With my marathon training now in full swing for Malaga, I was going to treat this as a fast training run. I had no real goal time in...
- Property Wrappers October 31, 2019First introduced in Swift 5.1, property wrappers allow us devs to add our own implementation details to a property before they are initialized. Currently, by delaying initialization we can declare a property as lazy, and then return our property when it’s first requested. Property wrappers...
- Shropshire Triathlon 2019 September 15, 2019A step up was on the cards for this race, as this would be my first shot at an Olympic distance triathlon after my sprint distance race that I did back in April. I did not have the best prep going into the race, as...
- Running Xcode Tests from CI August 3, 2019The ability to run a suite of tests is a great way to cut down on manual testing time, whilst still having the knowledge that any new changes have not broken the codebase. There are a number of different options when it comes to writing...
- Method Swizzling June 29, 2019When anyone mentions swizzling I automatically get worried. Method swizzling is an Objective C runtime feature which allows for the switching of method implementations. This means any function can theoretically be switch out for another one at run time. The Objective C runtime as a...