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
iOS 11 Layout Guidance About Safe Area for iPhone X
Disable Wkwebview for Opening Links to Redirect to Apps Installed on My Iphone
Passing Variables Between View Controllers
iOS Swift: How to Change the Font Style of a Certain Word in a String
How to Specify a Platform Target When Running Swift Test from the Cli
How to Get Index Path of Cell on Switch Change Event in Section Based Table View
Hashtags in Arabic Language Crashes the App
Removing Duplicate Objects from Fetch Based on Object Parameter Updated Swift
Completion Block for Popviewcontroller
Inputaccessoryview Docked at Bottom
How to Get Memory Usage in Swift
How to Use Http Live Streaming Protocol in iPhone Sdk 3.0
Responding to Ram Availability in iOS
How to Access an Xcassets Directory on the Filesystem
How to Force Uiviewcontroller Orientation
Audiokit: Using the New Aksequencer with Any Variety of the Callback Instruments