0

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.

Adrian
  • 19,440
  • 34
  • 112
  • 219

2 Answers2

3

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

Leo
  • 24,596
  • 11
  • 71
  • 92
-1

You hardcoded .One in your get function.

As, according to your comment, your complication with associated values serves no purpose, you should simplify:

enum Numbers: String {
    case One = "One"
    case Two = "Two"
    case Three = "Three"

    // default
    init() { self = .One }

    static let germanNumbers = [One: "Eins", Two: "Zwei", Three: "Drei"]
    var germanString: String { return Numbers.germanNumbers[self]! }
}

let num = Numbers.Three

print(num)              // "Three"
print(num.rawValue)     // "Three"

let defaultNum = Numbers()
print(defaultNum)       // "One"

print(num.germanString)        // "Drei"
print(defaultNum.germanString) // "Eins"
Mundi
  • 79,884
  • 17
  • 117
  • 140
  • I did that to return .One if there is no value. – Adrian Nov 14 '15 at 12:23
  • Please clarify what purpose you want to achieve with the associative value. – Mundi Nov 14 '15 at 12:29
  • I want to set an enum value. In my example I set it to .Three, but it prints .One (default value) . So when trying to read the value it found nil, that is why it prints default value. I don't know why the values wasn't set correctly. – Adrian Nov 14 '15 at 12:31