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.