Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass in a void block to objc_setAssociatedObject in swift

Tags:

ios

swift

I'm trying to add tap gesture support to UIView via an extension. This is pretty straight forward using Objective-C, but I'm getting the following error when trying to set the void return block on the runtime property.

error: type '() -> Void' does not conform to protocol 'AnyObject'

Here is the computed property:

var tapAction: (() -> Void)? {

    get {
        objc_getAssociatedObject(self, &AssociatedKeys.SNGLSActionHandlerTapBlockKey)
    }

    set {

        objc_setAssociatedObject(
            self,
            &AssociatedKeys.SNGLSActionHandlerTapBlockKey,
            newValue,
            UInt(OBJC_ASSOCIATION_COPY_NONATOMIC)
        )
    }
}

I've tried to set the tapAction as a typealias, but still receive the same error.

like image 808
Cory D. Wiles Avatar asked Nov 30 '22 00:11

Cory D. Wiles


1 Answers

The problem is closures are not objects that conform to AnyObject so you can't store them like that from Swift.

You could look into wrapping the closures in a class with a single property. Something like:

class ClosureWrapper {
  var closure: (() -> Void)?

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

var tapAction: (() -> Void)? {
  get {
    if let cl = objc_getAssociatedObject(self, "key") as? ClosureWrapper {
      return cl.closure
    }
    return nil
  }

  set {
    objc_setAssociatedObject(
      self,
      "key",
      ClosureWrapper(newValue),
      UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    )
  }
}

It's a little ugly and could use a nice typealias but it's one way to do it.

like image 63
gregheo Avatar answered Dec 10 '22 16:12

gregheo