Pass in a Type to a Generic Swift Extension, or Ideally Infer It

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

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.

Generic function taking a type name in Swift

Unfortunately, you cannot explicitly define the type of a generic function (by using the <...> syntax on it). However, you can provide a generic metatype (T.Type) as an argument to the function in order to allow Swift to infer the generic type of the function, as Roman has said.

For your specific example, you'll want your function to look something like this:

func findFirst<T>(in array: [Any], ofType _: T.Type) -> T? {
return array.lazy.compactMap { $0 as? T }.first
}

Here we're using compactMap(_:) in order to get a sequence of elements that were successfully cast to T, and then first to get the first element of that sequence. We're also using lazy so that we can stop evaluating elements after finding the first.

Example usage:

protocol SomeProtocol {
func doSomething()
}

protocol AnotherProtocol {
func somethingElse()
}

extension String : SomeProtocol {
func doSomething() {
print("success:", self)
}
}

let a: [Any] = [5, "str", 6.7]

// Outputs "success: str", as the second element is castable to SomeProtocol.
findFirst(in: a, ofType: SomeProtocol.self)?.doSomething()

// Doesn't output anything, as none of the elements conform to AnotherProtocol.
findFirst(in: a, ofType: AnotherProtocol.self)?.somethingElse()

Note that you have to use .self in order to refer to the metatype of a specific type (in this case, SomeProtocol). Perhaps not a slick as the syntax you were aiming for, but I think it's about as good as you're going to get.

Although it's worth noting in this case that the function would be better placed in an extension of Sequence:

extension Sequence {
func first<T>(ofType _: T.Type) -> T? {
// Unfortunately we can't easily use lazy.compactMap { $0 as? T }.first
// here, as LazyMapSequence doesn't have a 'first' property (we'd have to
// get the iterator and call next(), but at that point we might as well
// do a for loop)
for element in self {
if let element = element as? T {
return element
}
}
return nil
}
}

let a: [Any] = [5, "str", 6.7]
print(a.first(ofType: String.self) as Any) // Optional("str")

Swift Generic Type Inference

Since it seems you want findPath to be a method strongly connected to the type that conforms to NodeSolver, but wont use any instance of this concrete NodeSolver type in the findPath method itself, you might want to consider simply adding the general findPath method as a type method available by a default implementation for all types conforming to NodeSolver.

E.g.:

struct Node {}

protocol NodeSolver {
static func solve(_ nodes: [Node]) -> [Node]
static func findPath(nodes: [Node]) -> [Node]
}

extension NodeSolver {
static func findPath(nodes: [Node]) -> [Node] {
// hopefully some more logic ...
return Self.solve(nodes)
}
}

struct MyNodeSolver: NodeSolver {
// dummy solver
static func solve(_ nodes: [Node]) -> [Node] {
return nodes
}
}

let myNodes = [Node(), Node()]

// make use of the default implementation of `findPath` available
// to all types conforming to 'NodeSolver': this method itself
// make use of the concrete type-specific implementation of 'solve'
// in the types conforming to 'NodeSolver'.
let dummyPath = MyNodeSolver.findPath(nodes: myNodes)

I am handing a constrained type in the protocol I am specifying. And
the actual type in the method call.

findPath<NodeSolver1>(nodes)
findPath<NodeSolver2>(nodes)

Another workaround, possible closer to what you're trying to achieve, is to wrap the generic function into a generic type (say, struct) which holds a non-generic function findPath w.r.t. concrete versions of the generic type. If viewing the wrapped findPath from a viewpoint external to the owning type, the functions is generic w.r.t. the generic typeholder of the owning type.

E.g.:

struct Node {}

protocol NodeSolver {
static func solve(_ nodes: [Node]) -> [Node]
}

struct NodeSolverA: NodeSolver {
static func solve(_ nodes: [Node]) -> [Node] {
return nodes
}
}

struct NodeSolverB: NodeSolver {
static func solve(_ nodes: [Node]) -> [Node] {
return nodes.reversed()
}
}

// instead of a generic function, wrap a concrete function
// in a generic type
struct AnyNodeSolver<T: NodeSolver> {
static func findPath(nodes: [Node]) -> [Node] {
return T.solve(nodes)
}
}

let myNodes = [Node(), Node()]

let dummyPathA = AnyNodeSolver<NodeSolverA>.findPath(nodes: myNodes)
let dummyPathB = AnyNodeSolver<NodeSolverB>.findPath(nodes: myNodes)

Protocol with generic typealias used by an extension

The extension method parsed() cannot infer that the generic type T is the same type as the immutable that you assign the result of the call to.

You can solve this by supplying the parsed(..) method with the actual parser type as a parameter. You needn't actually make explicit use of this parameter (hence internal name omitted using _), and its sole purpose in this context is to infer the type of the generic T.

// ...

extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
func parsed<T: ParserType>(_: T.Type) -> ([T.ParsedObjectType], PaginationContext?) {
let paginatedParser = PaginatedParser<T>()
return try! paginatedParser.parse(self as! AnyObject)
}
}

With this, you needn't explicitly supply the type of result as it can be inferred from the return of parsed(..). Note that I've also added a limitation in the extension for keys that conform to StringLiteralConvertible (String, among others, see language ref.) and for AnyObject values. You shouldn't make extensions more general than they need to be.

Example usage:

let thing2 = [String: AnyObject]()
let result = thing2.parsed(DogParser)

print(result.dynamicType)
/* (Array<Dog>, Optional<PaginationContext>), OK */

swift generic sequence with identifiable constraint on elements

The first one is almost correct. To refer to the element type of a Sequence, use .Element, not .Type. .Type refers to the metatype of a type.

struct GridView<ListType>: View where ListType: Sequence, ListType.Element: Identifiable

Can I assign a default type to generic type T in Swift?

There's no support for default generic arguments, but you can fake it by defining the default init() on a type-constrained extension, in which case the compiler will be smart enough to use that type. E.g.:

class MyManager<T> {
let instance: T

init(instance: T) {
self.instance = instance
}
}

extension MyManager where T == NSObject {
convenience init() {
self.init(instance: NSObject())
}
}

And now you can initialize the type with no argument and it will default to MyManager<NSObject>:

let mm1 = MyManager(instance: "Foo") // MyManager<String>
let mm2 = MyManager(instance: 1) // MyManager<Int>
let mm3 = MyManager() // MyManager<NSObject>

SwiftUI uses this technique quite a lot.

Is it necessary to pass a type parameter to static functions in a generic class?

GenericClass is not a concrete type in Swift. There's no way to talk about it any more than you can talk about the type Array without providing its type parameter. (The specific feature Swift lacks that would allow this is called "higher-kinded types.")

Static methods are tied to the concrete type (GenericClass<T>) not to the higher-kinded type (GenericClass). In order to access the static methods, it needs to know the actual type you want.

While it's true you're not currently using the type parameter in this class method, there is nothing stopping you (or a subclass!) from doing so. Since you have no way to promise that T will never occur in this method, there's no way for Swift to allow you to ignore it.

(Swift) extension of SSP where S is a generic structure and P is a type-constrained protocol

Swift doesn't support this syntax currently (see SR-3716). You need to attach the constraint to each extension method directly:

extension S {
func f<U>() where T == S<U>, U: P {}
}

With that in place, you'll be able to call f() for some values, but not others:

extension String: P {
public typealias A = Int
}

S<Int>().f() // Fails
S<S<Int>>().f() // Fails
S<String>().f() // Fails
S<S<String>>().f() // Succeed

Unfortunately, autocomplete will still offer f() in places it's not allowed, and the diagnostic is not ideal:

Generic parameter 'U' could not be inferred
Instance method 'f()' requires the types 'Int' and 'S<U>' be equivalent

I believe there is work going on to improve the developer experience around this, but the compiler will enforce it.



Related Topics



Leave a reply



Submit