@objc compatibility problem

Now, I may be missing something really simple here but the following simplified experimental code is similar to production code that I know works.

The reason for the experimental code is to add new internal functionality to some protocols that I have added to the framework. When I started to get problems, I stripped out everything except what you see here to see what is provoking the, previously unknown, error

public class Interactor: NSObject { }

@IBDesignable
public class PropertyInteractor<subjectT> : Interactor
{
  @IBOutlet var propertyName: String!
}

public class LabelInteractor<subjectT, valueT> : PropertyInteractor<subjectT> { }

public class Model<subjectT>
{
  public var subject: subjectT
  
  public init(subject: subjectT)
  {
    self.subject = subject
  }
}

public final class ValueModel : Model<Int>
{
  public override var subject: Int
  {
    get
    {
      return super.subject
    }
    set
    {
      super.subject = newValue
    }
  }
}

public typealias ModelIntegerLabelInteractor = LabelInteractor<ValueModel, Int>

But, in a UIViewController, I'm doing this :

class ViewController: UIViewController
{
  …
  
  @IBOutlet weak var interactor: ModelIntegerLabelInteractor!
  
  …
}

… and getting the error :

@IBOutlet property cannot have non-'@objc' class type 'ModelIntegerLabelInteractor' (aka 'LabelInteractor<ValueModel, Int>')

I'm introducing NSObject, @IBDesignable and @IBInspectable at exactly the same places as in my working production code.

Any clues please ?

Forget it ! Thanks for the "rubber duck" consultation. The answer was that I was holding interactors in non-generic-derived base classes and having to downcast whenever I needed them in code. In the experimental code I forgot that and used the generic-derived final class, thinking that, now that it was no longer generic in itself, that would satisfy the @objc requirement.

Nonetheless, I'll leave the post here in case it helps anyone less experienced than me from tearing their hair out.

As Homer would say - Doooohhhh!!!

1 Like

OK, here's a solution, straight out of the Design Patterns book, that solves everything. The Wrapper or Adapter pattern means I can have a generic hierarchy on the inside of a non-generic, @objc-compliant wrapper :

open class Interactor
{
  func activate() { }
}

open class PropertyInteractor<subjectT> : Interactor
{
  public var subject: subjectT?
  
  public var propertyName: String?
}

open class LabelInteractor<subjectT, valueT> : PropertyInteractor<subjectT>
{
  public var view: UILabel!  
}

// model classes stay the same

@IBDesignable
public class ModelIntegerLabelInteractor : NSObject
{
  private lazy var _interactor: LabelInteractor<ValueModel, Int> = .init()
  
  var subject: ValueModel?
  {
    get
    {
      return _interactor.subject
    }
    set
    {
      _interactor.subject = newValue
      
      _interactor.propertyName = propertyName
    }
  }

  @IBInspectable
  public var propertyName: String?
  {
    get
    {
      return _interactor.propertyName
    }
    set
    {
      _interactor.propertyName = newValue
    }
  }

  @IBOutlet weak var view: UILabel!
  {
    get
    {
      return _interactor.view
    }
    set
    {
      _interactor.view = newValue
    }
  }
  
  func activate()
  {
    _interactor.activate()
  }
}

It would be marvellous if IB could cope with generic-derived types but, in the meantime, thanks to the Wrapper/Adapter Pattern, all that great generic stuff doesn't have to go to waste and ensures everything is more typesafe than using Any, etc all over the place.