Pass in a type to a generic Swift extension, or ideally infer it

五迷三道 提交于 2019-11-27 13:34:52

Simply use the .Type:

internal func siblings<T>( something : T.Type)->([T]) {
    ...
}

Afterwards for f in self.siblings(Fancy) should work exactly as expected.

Full working example:

class Fancy : UIView {}

public extension UIView {
    internal func siblings<T>( _ : T.Type)->([T]) {
        return (self.superview!
            .subviews
            .filter { $0 != self }
            .flatMap { $0 as? T }
        )
    }
}

let superView = UIView()
let view = UIView()
superView.addSubview(view)
superView.addSubview(UIView())
superView.addSubview(Fancy())

print(view.siblings(Fancy))

Correctly outputs the one Fancy view!


To address the requested addition for optionally using the explicit type parameter or take effect of the type inference of the compiler. You can create a second method in the same extension

internal func siblings<T>()->([T]) {
    return siblings(T)
}

That way providing a explicit type parameter calls method one, omitting it will require you to make it inferable and will call the second function which in terms calls the first one internally.


Or, you can use the far more swifty way and make the explicit type argument an optional with default nil. That, remarkably, will force the inference in case of omitting the type argument:

// power extension, it provides both infered or stated typing
internal func siblings<T>(_ : T.Type? = nil) -> ([T]) {
    return (self.superview!
        .subviews
        .filter { $0 != self }
        .flatMap { $0 as? T }
        )
}

That will enable you to call the method either via

for f in self.siblings(Fancy)

or even

for f : Fancy in self.siblings()

Both will work while still only defining one function.

Similar answer to what's been said previously, but a little more streamlined and without having to pass anything or iterate over the subviews more than once:

extension UIView {
    internal func siblings<T: UIView>() -> [T] {
        return superview?.subviews.flatMap {return ($0 == self) ? nil : ($0 as? T) } ?? []
    }
}

or my preference using optionals:

internal func siblings<T: UIView>() -> [T]? {
        return superview?.subviews.flatMap {return ($0 == self) ? nil : $0 as? T } 
}

Example Usage:

class ExampleView: UIView {

    func getMatchingSiblings(){
        let foundSiblings: [ExampleView] = siblings()
    }

    //or with the for loop in the question:
    for item: ExampleView in siblings() {

    }
}

When dealing with generics, you simply need one instance of the generic type in the signature of the method. So if you have either a parameter, or return type that uses the generic, you don't need to pass the type.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!