Can I specialize a generic function on a type passed as a parameter? - swift

I want to be able to specialize a generic function by passing a type as an argument, instead of having to declare a variable having the desired type and writing an assignment to that variable.
In my use case, I'm walking up the responder chain looking for an object that conforms to a certain protocol. If found, I'd like to call a method on that object.
I'm trying to do it in a "swift-y" (i.e. type safe) way.
The code I'm currently using looks like this:
if let r:UndoManager = self.findResponder() {
r.undo(...)
}
but that makes it hard to chain in a statement.
I want to do something more succinct, like the following, passing the desired protocol as an argument to my function.
self.findResponder( UndoManager )?.undo(...)
In this example, say I have protocol UndoManager, defined as
protocol UndoManager {
func undo(...)
}
Also, my declaration of findResponder() currently looks like
public extension NSReponder {
public func findResponder<T>() -> T? {
...
}
}

If you want to do self.findResponder( UndoManager )?.undo(...) instead of (findResponder() as Undo?)?.undo(...), the method signature would be:
public func findResponder<T>(_: T.Type) -> T? {
// ...
}

Related

How can I use a protocol's type in method signature?

I have a function like this:
// A generic function that can fetch values from the store given a type
// Corresponds to a table and it's rows
func fetch<T: FetchableRecord>(for theType: T.Type) -> [T] {
// ... fetch from a store using the type
// This compiles fine
}
How can I use this with a collection of types?
Ideally, if I have some conformers:
struct ModelA: FetchableRecord { }
struct ModelB: FetchableRecord { }
then I would like to be able to do:
let modelTypes: [FetchableRecord.Type] = [ModelA.self, ModelB.self]
modelTypes.forEach {
fetch(for: $0) // xxx: does not compile: "Cannot invoke 'fetch' with an argument list of type '(for: FetchableRecord.Type)'"
}
At the very least, I would like to figure why this would not be possible.
Thank you.
The reason for the error is FetchableRecord.Type is not the same as ModelA.Type or ModelB.Type. Even if both of the structs conform to FetchableRecord protocol, constraining the models (by conforming to a certain protocol) does not affect the "Types", more technically speaking:
ModelA.self == FetchableRecord.self OR ModelB.self == FetchableRecord.self is false.
In order to resolve this issue, you could implement the method's signiture as:
func fetch(for theType: FetchableRecord.Type) -> [FetchableRecord] {
// ...
}
therefore, your code:
let modelTypes: [FetchableRecord.Type] = [ModelA.self, ModelB.self]
modelTypes.forEach {
fetch(for: $0)
}
should work. At this point, you are dealing with it "Heterogeneously" instead of "Homogeneously".
Furthermore, if that makes it more sensible, note that when calling fetch method as per your implementation, it is:
the parameter type is FetchableRecord.Protocol. However, as per the above-mentioned implementation (in this answer), it is:
the parameter type is FetchableRecord.Type, which is the wanted result.

Constraining associated types in Swift protocol

I'm writing a little code snippet to learn about how associated types work, but I've come across an error I'm not sure how to interpret. The code I've written is posted below for reference.
// A basic protocol
protocol Doable {
func doSomething() -> Bool
}
// An extension that adds a method to arrays containing Doables
extension Array where Element: Doable {
func modify(using function:(Array<Doable>)->Array<Doable>) -> Array<Doable> {
return function(self)
}
}
// Another protocol with an associated type constrained to be Doable
protocol MyProtocol {
associatedtype MyType: Doable
func doers() -> Array<MyType>
func change(_:Array<MyType>) -> Array<MyType>
}
// An simple extension
extension MyProtocol {
func modifyDoers() -> Array<MyType> {
return doers().modify(using: change)
}
}
I've constrained MyType to be Doable, but the compiler complains that it cannot convert (Array<Self.MyType>) -> Array<Self.MyType> to expected argument type (Array<Doable>) -> Array<Doable>. Can anybody explain what's going on here and how I can make the compiler happy?
As the error message says, the modify function expects arguments with the type Array<Doable> and you're passing arguments with the type Array<MyType>.
The issue stems from the definition of modify, where you're explicitly using Doable in the parameters, which excludes all other types but Doable – and as associated types are not typealiases, MyType can't be converted to Doable.
The fix is to change all occurrences of Doable in the modify function to Element, as is portrayed in the Swift documentation: Extensions with a Generic Where Clause.

Parameterizing closures as generic based on parameter-list/return type

Is there a way to make a generic type signature for a closure such that I can later call it generically? In particular my question is how to deal with an unknown number of arguments.
I have an object that I’d like to call a series of closures on an update, and I’d like other objects to be able to register closures that they’d like to be called with that first object.
Closures aren’t hashable, but I’d like to be able to also unregister a closure, so I wound up creating a custom type to handle this based on a dictionary:
//T is the block signature such as Double->()
struct ClosureCollection<T> : SequenceType {
private var idx=0
var closureDict:[Int:(T,NSOperationQueue)]=[:]
mutating func addClosure(b:T) -> Int {
return addClosure(NSOperationQueue.mainQueue(),b)
}
mutating func addClosure(q:NSOperationQueue, _ b:T) -> Int {
closureDict[idx]=(b,q)
idx+=1
return idx-1
}
mutating func dropClosure(k:Int) {
closureDict.removeValueForKey(k)
}
func generate() -> AnyGenerator<(T,NSOperationQueue)> {
var dgen=closureDict.generate()
return AnyGenerator {
return dgen.next()?.1
}
}
}
This lets me use the collection in code like this:
Declare it:
private var distributionPoints=ClosureCollection<(CMDeviceMotion?,NSError?) -> ()> ()
Use it:
for (p,q) in distributionPoints {
q.addOperationWithBlock {p(dm,error)}
}
So far, all’s good but it requires the caller to follow a pretty specific pattern to use the collection that I'd like to hide. I’d like to extend this idea to where I can ask the collection to run the for loop itself using syntax something like
distributionPoints.runAllWith(dm,error)
The problem is with the runAllWith signature-- my current implementation is generic over the full closure signature because I don't know how to make the number of arguments to the closure generic. I suspect this can be done if I know the blocks accept one argument and return one argument, for example, by using two type placeholders, say T for the argument and U for the return value.
What I don’t seem able to do is make this generic across an unknown number of parameters. The type I’m creating doesn’t care what the structure of the block is, it just wants to accept closures with a certain signature and then provide a mechanism to call them by exposing an API that depends on the closure signature.
I don’t think there’s support for variadic generic parameters, so I can’t go down that road. Tuple-splat was deprecated.
Is there a way to do this without requiring the caller to bundle arguments into a tuple and then requiring a wrapper around each closure to unwrap a tuple of arguments and do a hand-crafted tuple splat?

Neat method signature to find subclasses of a given type

I have a method that I have implemented, but am having trouble getting the method signature to be elegant.
The method returns all classes which are a subclass of a specified class. My current method signature looks like :
public func allSubclassesOf(baseClass: AnyClass) -> [AnyClass]
which is fine, apart from the return type being AnyClass which means I always end up with a messy cast like this:
allSubClassesOf(UIView).forEach { (subclass:AnyClass) in
UIView *sigh = subclass as! UIView //!< Gross and unnecessary
...
}
This seems like something that generics should be able to solve :)
Things I've tried :
public func allSubclassesOf<T>() -> [T]
Nope, you're not allowed to add generics to a function like that.
extension NSObject {
class func allSubclasses() -> [Self]
}
Nope, Self isn't available here.
Does anyone know how I can pass a type into this method and have the compiler know what type the returning array would hold; removing the need for a cast?
I am not sure of the implementation of your method but you would need to do something like the following.
What I am doing here is applying a generic type to the method function then saying that I would expect the object type as an argument and return instances of the type in an array.
You could add it as an extension, however without more examples for your code I can't help any further
func subclasses<T>(type: T.Type) -> [T] {
....
}
subclasses(UIView).forEach { (view: UIView) in
print(view)
}

How do I declare a function that takes *any* block/closure as parameter?

I want to pass any block around as a parameter, without wanting to know the precise block parameter/return type.
func myFunc(block:(xxx) -> yyy) -> AnyObject {
...
}
So xxx in my code should match any number of parameters (including none) of any type. And yyy could be anything from void to AnyObject to an NSObject.
You can make a generic function:
func myFunc<A,B>(block:A -> B) -> AnyObject {
...
}
Unfortunately, you can't do that in Swift. Function Types are defined by their parameters and return types and there is no general function type. Functions also don't conform to Any or AnyObject, so you also can't use those either.
In Swift it's still possible to use the Selector. Maybe you can achieve what you want using that. See sample below:
func myFunc(selector:Selector) -> AnyObject {
self.performSelector(selector)
}
func someSelector() -> String {
return "test"
}
var result: AnyObject = myFunc(Selector("someSelector"))