DEV Community

Giuseppe Travasoni
Giuseppe Travasoni

Posted on

1

Exploring Dynamic Method Injection in Swift

Introduction

Hello Swift enthusiasts! In today's article, we're diving into an intriguing aspect of Swift programming - dynamic method injection. This technique allows developers to add methods to classes or objects at runtime, offering a flexible approach to managing functionalities. We'll explore this concept through a code snippet that injects a method from one class into another.

What is Method Injection?

Method Injection is a process where you dynamically add new methods to a class or an instance of a class at runtime. This is not typically a Swift-native approach, as Swift emphasizes type safety and compile-time checks. However, Swift's interoperability with Objective-C runtime offers a backdoor to achieve this.

The Code Breakdown

Here's a brief overview of the code snippet we're examining:

Class Definitions

  • ClassA: Contains a hello() method that prints the class's name. hello() method must have @objc attribute because it will be user with Objective-C Runtime soon.
  • ClassB: An empty class without any methods.
class ClassA {
    @objc
    func hello() {
        print("Hello, i'm " + String(describing: type(of: self)))
    }
}

class ClassB { }
Enter fullscreen mode Exit fullscreen mode

Method Injection Function

  • useMethod(sel:fromClass:inObject:): Injects the method from fromClass into inObject.
  • It employs class_getInstanceMethod and unsafeBitCast to dynamically add the method to the object.
func useMethod(sel: Selector, fromClass: AnyClass, inObject: AnyObject) {

    guard let meth = class_getInstanceMethod(fromClass, sel) else {
        return
    }

    typealias ClosureType = @convention(c) (AnyObject, Selector) -> Void
    let imp = method_getImplementation(meth)
    let callMethod: ClosureType = unsafeBitCast(imp, to: ClosureType.self)
    callMethod(inObject, sel)
}
Enter fullscreen mode Exit fullscreen mode

Implementation

  • An instance of ClassA is created and its hello() method is called.
  • An instance of ClassB is then created, and the hello method from ClassA is injected and executed on it using useMethod.
let a = ClassA()
a.hello()

let b = ClassB()
useMethod(sel: #selector(ClassA.hello), fromClass:ClassA.self, inObject: b)
Enter fullscreen mode Exit fullscreen mode

Deep Dive into the Function

The useMethod function is where the magic happens. Let’s dissect it:

  • It first checks if the method exists in the source class using class_getInstanceMethod.
  • Once the method is obtained, it retrieves its implementation (IMP).
  • This implementation is then cast to a closure of type @convention(c) (AnyObject, Selector) -> Void using unsafeBitCast.
  • Finally, the method is invoked on the target object.

You can find full playground in this gist.

Why is This Important?

This method showcases an advanced level of Swift's capabilities, blending Swift's modern features with the underlying Objective-C runtime. It demonstrates how Swift can handle dynamic behavior, a feature often associated with more dynamic languages like Python or JavaScript.

Cautions

While powerful, method injection should be used judiciously. It bypasses Swift's type safety and can lead to unpredictable behaviors or crashes if not handled properly. Always ensure that the method and the target object are compatible.

Conclusion

Dynamic method injection in Swift, while not a common practice, opens doors to a range of possibilities for developers willing to explore the depths of the language. It exemplifies Swift's versatility and its seamless blend with Objective-C runtime, offering a unique approach to solving complex programming challenges.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay