Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Say you have

 class Fancy:UIView

you want to find all sibling Fancy views. No problem...

    for v:UIView in superview!.subviews
        {
        if let f = v as? Fancy
            { f.hungry = false }
        }

So, try an extension,

public extension UIView
    {
    internal func fancySiblings()->([Fancy])
        {
            return (self.superview!
                .subviews
                .filter { $0 != self }
                .flatMap { $0 as? Fancy }
                )
        }
    }

Awesome, you can now

    for f:Fancy in self.fancySiblings()
        { f.hungry = false }

Fantastic.

But,

How to generalize that extension to work with any UIView subtype?

Ideally, can the extension infer the type, even? As well as taking a type?

So, something like ...

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

and then you could call it something like this ...

    for f in self.siblings(Fancy)
    for p in self.siblings(Prancy)
    for b in self.siblings(UIButton)

How can you "tell" a generic extension the type to use, like that??

It seems you can "infer it backwards",

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


    for f:Fancy in self.incredible()

    for p:Prancy in self.incredible()

Which is amazing but doesn't work the other way.

You can even...

    self.siblings().forEach{
        (f:Fancy) in
        d.hasRingOn = false
        }

So I would still like to know how to "pass in" a type something like for f in self.siblings(Fancy) and, ideally, even infer it also.

like image 393
Fattie Avatar asked May 15 '16 15:05

Fattie


People also ask

What is a generic type Swift?

Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.

What is extension used for in Swift?

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you don't have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C.

How do you declare a generic variable in Swift?

In Swift, we can create a function that can be used with any type of data. Such a function is known as a Generic Function. Here's how we can create a generic function in Swift: // create a generic function func displayData<T>(data: T){ ... }


2 Answers

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.

like image 158
luk2302 Avatar answered Oct 16 '22 10:10

luk2302


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.

like image 31
GetSwifty Avatar answered Oct 16 '22 11:10

GetSwifty