简体   繁体   中英

Swift Generics: Extending a non-generic type with a property of generic type where the generic parameter is the extended type

Problem

I have a type that takes one generic parameter that is required to inherit from UIView :

class Handler<View: UIView> {
   ...
}

Now, I want write a UIView extension to provide a property that returns Handler and uses Self as the generic parameter, so that in subclasses of UIView I'd always get the handler of type Handler<UIViewSubclass> :

extension UIView {
   var handler: Handler<Self>? { return nil }
}

However this does not compile:

Covariant 'Self' can only appear at the top level of property type

I have also tried to define a protocol HandlerProvider first:

public protocol HandlerProvider {
    associatedtype View: UIView

    var handler: Handler<View>? { get }
}

(so far so good) and then extend UIView with that protocol:

extension UIView: HandlerProvider {
    public typealias View = Self

    public var handler: Handler<View>? { return nil }
}

But that does not compile either:

Covariant 'Self' can only appear as the type of a property, subscript or method result; did you mean 'UIView'?

Question

Is there a way in Swift to use Self as a generic parameter for properties in extension?

Here is possible approach (to think with generics in a bit different direction).

Tested with Xcode 11.4 / swift 5.2

// base handling protocol
protocol Handling {
    associatedtype V: UIView

    var view: V { get }
    init(_ view: V)

    func handle()
}

// extension for base class, will be called by default for any
// UIView instance that does not have explicit extension 
extension Handling where V: UIView {
    func handle() {
        print(">> base: \(self.view)")
    }
}

// extension for specific view (any more you wish)
extension Handling where V: UIImageView {
    func handle() {
        print(">> image: \(self.view)")
    }
}

// concrete implementer
class Handler<V: UIView>: Handling {
    let view: V
    required init(_ view: V) {
        self.view = view
    }
}

// testing function
func fooBar() {
    // handlers created in place of handling where type of
    // handling view is know, so corresponding handle function
    // is used
    Handler(UIView()).handle()
    Handler(UIImageView()).handle()
}

Output:

演示

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM