Uzaak
Uzaak

Reputation: 578

Dynamically defined Function-type vars

I am trying to extend a UIKit class (which I can't edit normally) by creating a new function and a new Function-type variable, which is going to use ObjC Runtime to make it look&feel like a stored property.

extension UITextField {

    private struct DynamicallyDefinedVars {
        static var oneVar = "oneVar"
    }

    var oneVar: ((String?)->Bool)? {
        get{
            return objc_getAssociatedObject(self, &DynamicallyDefinedVars.oneVar) as? (String?)->Bool
        }
        set{
            if let newValue: AnyObject = newValue {
                objc_setAssociatedObject(self, &DynamicallyDefinedVars.oneVar, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }

    func callTheVarFunc() -> Bool {
        if let oneVar = oneVar {
            return oneVar("Foo")
        }

        return true
    }
}

What I hope to achieve:

var foo: UITextField

foo.oneVar = { (bar: String?) -> Bool in
  return true
}

if foo.callTheVarFunc {
  doSomething
}

But I am getting the following error:

Cannot convert value of type '((String?) -> Bool)?' to specified type 'AnyObject?'

It would work fine if oneVar was typed something like String or an array of sorts, but I see the Function Types are not included in AnyObject, thus giving me issues when trying to objc_setAssociatedObject. Any thoughts on how I can get the desired behaviour (through extensions, without subclassing)? Each instance has to have a different oneVar value to be used with the callTheVarFunc function.

Upvotes: 1

Views: 62

Answers (1)

Alessandro Ornano
Alessandro Ornano

Reputation: 35402

I've just seen this problem, in Swift closures cannot be casted to AnyObject so you can workaround this annoying thing creating a custom class:

extension UITextField {

    class CustomClosure {
        var closure: ((String?)->Bool)?

        init(_ closure: ((String?)->Bool)?) {
            self.closure = closure
        }
    }

    private struct DynamicallyDefinedVars {
        static var oneVar = "oneVar"
    }

    var oneVar: ((String?)->Bool)? {
        get{
            if let cl = objc_getAssociatedObject(self, &DynamicallyDefinedVars.oneVar) as? CustomClosure {
             return cl.closure
            }
        return nil
        }
        set{
            objc_setAssociatedObject(self, &DynamicallyDefinedVars.oneVar,CustomClosure(newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    func callTheVarFunc() -> Bool {
        if let oneVar = oneVar {
            return oneVar("Foo")
        }

        return true
    }
}

Upvotes: 1

Related Questions