Dealing with loss of observers on protocol properties with default implementations


(Neil) #1

Hi folks,

I'm curious to know how others have handled the loss of property observers when a protocol property has a default implementation.

For example, consider the following simple protocol declaration:

protocol Widget : class {
    associatedtype IdentifierType
    var identifier: IdentifierType { get set }
}

extension Widget {
    var identifier: IdentifierType {
        get {
            return ...
        }
        set {
            ...
        }
    }
}

A class conforming to the Widget protocol cannot leverage property observers on the identifier property without overriding the default implementation:

class WidgetFactory: Widget {
    typealias IdentifierType = Int

    // Overrides the default implementation
    var identifier: Int {
        didSet {
          // ...
        }
    }
}

This is problematic when the default implementation goes beyond merely getting/setting a backing variable. The only solution I've come up with is to mandate a willSet/didSet function pair for each protocol property:

protocol Widget : class {
    associatedtype IdentifierType
    var identifier: IdentifierType { get set }

    func willSetIdentifier(_ newValue: IdentifierType)
    func didSetIdentifier(_ newValue: IdentifierType)
}

extension Widget {
    var identifier: IdentifierType {
        get {
            return ...
        }
        set {
            ...
        }
        willSet {
            willSetIdentifier(newValue)
        }
        didSet {
            didSetIdentifier(identifier)
        }
    }
}

I find this to be an ugly solution. Particularly so since non-objc protocols don't support optional members. Compare this to using a base class:

protocol Widget : class {
    associatedtype IdentifierType
    var identifier: IdentifierType { get set }
}

class BaseWidget : Widget {
    typealias IdentifierType = Int

    var identifier: Int {
        get {
            return ...
        }
        set {
            ...
        }
    }
}

class WidgetFactory : BaseWidget {
    // This doesn't override the getter/setter implementation in the base class
    override var identifier: Int {
        willSet {
            ...
        }
    }
}

Has anyone else faced this situation? If so, what was your solution?

-- Neil


(Tino) #2

I find this to be an ugly solution. Particularly so since non-objc protocols don't support optional members. Compare this to using a base class:

If inheritance works well, I wouldn't insist on protocols just for the sake of it.
This wont help with structs, but your example uses a class… and if you can't use inheritance for other reasons, you could try composition instead.