Skip to content

πŸ”— Swift Macro for allowing variable declarations even in class extensions

License

Notifications You must be signed in to change notification settings

p-x9/AssociatedObject

Repository files navigation

AssociatedObject

Swift Macro for allowing variable declarations even in class extensions. It is implemented by wrapping objc_getAssociatedObject/objc_setAssociatedObject.

Github issues Github forks Github stars Github top language

Installation

SPM

.package(url: "https://github.com/p-x9/AssociatedObject", from: "0.10.3")

CocoaPods

Add below to your Podfile.

pod 'AssociatedObject', git: 'https://github.com/p-x9/AssociatedObject', tag: '0.10.3'

After pod install, you can use this Macro in your project.

Additionally, if you encounter build error like Expansion of macro 'AssociatedObject' did not produce a non-observing accessor. You should check your project setting Build Settings-OTHER_SWIFT_FLAGS.

There should be additional flags like so. Alt text

If not, you can add these two lines by yourself.

-load-plugin-executable
${PODS_ROOT}/AssociatedObject/Binary/AssociatedObjectPlugin#AssociatedObjectPlugin

Usage

For example, you can add a new stored property to UIViewController by declaring the following

import AssociatedObject

extension UIViewController {
    @AssociatedObject(.retain(nonatomic))
    var text = "text"

    /* OR */

    @AssociatedObject(.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    var text = "text"

    static var customKey = ""
    @AssociatedObject(.OBJC_ASSOCIATION_RETAIN_NONATOMIC, key: customKey)
    var somevar = "text"
}

Declared properties can be used as follows

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        print(text) // => "text"

        text = "hello"
        print(text) // => "hello"
    }

}

willSet/didSet

Properties defined using @AssociatedObject can implement willSet and didSet. In swift, it is not possible to implement willSet and didSet at the same time as setter, so they are expanded as follows.

@AssociatedObject(.copy(nonatomic))
public var hello: String = "こんにけは" {
    didSet {
        print("didSet")
    }
    willSet {
        print("willSet: \(newValue)")
    }
}

// ↓↓↓ expand to ... ↓↓↓
public var hello: String = "こんにけは" {
    get {
        objc_getAssociatedObject(
            self,
            &Self.__associated_helloKey
        ) as? String
        ?? "こんにけは"
    }

    set {
        let willSet: (String) -> Void = { [self] newValue in
            print("willSet: \(newValue)")
        }
        willSet(newValue)

        let oldValue = hello

        objc_setAssociatedObject(
            self,
            &Self.__associated_helloKey,
            newValue,
            .copy(nonatomic)
        )

        let didSet: (String) -> Void = { [self] oldValue in
            print("didSet")
        }
        didSet(oldValue)
    }
}

License

AssociatedObject is released under the MIT License. See LICENSE