@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.

Terms of Service

Privacy Policy

Cookie Policy