Search code examples
iosswiftassociated-object

How to use associated objects with enums?


I have a ViewController to which I added two new properties using associated objects: an enum and a string (string version is taken from here)

Here is my example code:

extension UIViewController {

    private struct AssociatedKeys {
        static var handle = "handle"
    }

    enum CustomStringEnum: String {
        case One = "One"
        case Two = "Two"
        case Three = "Three"
    }

    var customEnum: CustomStringEnum {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.handle) as? CustomStringEnum ?? .One
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.handle, newValue.rawValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var descriptiveName: String {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.handle) as! String
        }

        set {
            objc_setAssociatedObject(
                self,
                &AssociatedKeys.handle,
                newValue as NSString?,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let vc = UIViewController()
        vc.customEnum = .Three
        vc.descriptiveName = "Three"

        print(vc.customEnum.rawValue) // -> This prints "One"
        print(vc.descriptiveName)     // -> This prints "Three"
    }
}

The string version its working correctly, but the enum one doesn't. And I am not sure what the problem is.

It is a problem with the objc_getAssociatedObject or the objc_setAssociatedObject. The get version seems to be nil all the time, so the default value of One is returned.


Solution

  • Change your code to this

    extension UIViewController {
    
    private struct AssociatedKeys {
        static var handle = "handle"
        static var enumContext = "enumContext"
    }
    
    enum CustomStringEnum: String {
        case One = "One"
        case Two = "Two"
        case Three = "Three"
    }
    
    var customEnum: CustomStringEnum {
        get {
            let rawvalue = objc_getAssociatedObject(self, &AssociatedKeys.enumContext)
            if rawvalue == nil{
                return .One
            }else{
                return CustomStringEnum(rawValue: rawvalue as! String)!;
            }
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.enumContext, newValue.rawValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    var descriptiveName: String {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.handle) as! String
        }
    
        set {
            objc_setAssociatedObject(
                self,
                &AssociatedKeys.handle,
                newValue as NSString?,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }
    }
    }
    

    Then it will work