Get the class or struct that conforms to a protocol for use in function using Generics - swift

My question if you have an Array of objects that conforms to a protocol. I want to iterate over the array calling a method on each member in the array. However the method I want to call is static and uses generics. I need to get the class of the element in the array to do this. how do you get the class of that object? Is it possible?
I am writing a library of generic functions in Swift.
I have a protocol called DBAble which has as function:
static func get<T: DBable >(id:Int) -> T?
I have an array of objects that conform to DBAble:
let objs:[DBAble] = []
I want to iterate over the array and call:
for obj in objs {
obj.get(id: anInt)
}
however I am getting this message:
Static member 'get' cannot be used on instance of type 'DBable.Protocol'
Is there a way of finding the class (or type of struct) of the object that conforms to the protocol? I understand that I can do:
if obj is User {
}
however this is not the solution I am looking for.

The problem with the application approach it that the type T in the bellow method
static func get<T: DBable >(id:Int) -> T?
has to be known at compile time, whereas the the dynamicType will give you the type at run time.
For anyone else considering this question the answer is no it is not possible to find the type for use in a generic function at runtime. As explained in the comments by #hamish
"element.dynamicType is the actual type of a given element in your array. Your get(_:) method has a generic parameter T that must be known at compile time"

Related

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?

Pass only specific types as argument to function

I have a function which is being repeated about 8-9 times and I'm trying to cut down on redundancy.Is it possible to create a function which take spefic types and returns an array of the initlizated type using the json object being sent.
Current function
static func initArray(json: JSON)-> [Event]{
var array = [Event]()
json.forEach(){
array.append(Event.init(json: $0.1))
}
return array
}
Desired function
static func initArray<T> (type: T, json: JSON)-> [T]{
var array = [T]()
//I get stuck here im not too sure how to initlize the type
//Thats why im wondering if it's possible to pass speific types
//to the function
return array
}
You initialize an instance of T just like an instance of any known class, with whatever initializers are available to it. To know what your options are, you generally need to constraint T in some form. The way I typically go about this is to define a protocol that all the types I care about passing to the function adopt. In your case you would put specific initializers in your protocol. Then constraint T to be of that type:
protocol SomeProtocol {
init(json: JSON)
}
class someClass {
static func initArray<T:SomeProtocol>(type: T, json: JSON) -> [T] {
// Create your objects, I'll create one as an example
let instance = T.init(json: json)
return [instance]
}
}
Constraining generic types can be more complex than what I've shown, so I'd recommend checking out the Type Constraints section of the Generics chapter of The Swift Programming Language for more information.

Swift type conformance to Any

Does every type in swift, apart from functions, conform to Any? I understand that AnyObject represents all class types and Any is at a higher level than that, representing all other types. However I came across a situation recently where I had a function that looked like:
func myFunFunction(someArgument: Any) {
...
}
And when calling it with a String argument: myFunFunction("This is a string") I get an error saying that type String can't be converted to type Any.
Edit:
The above example is just that, an example. I wanted to simplify the code just to get an answer but here's my actual code:
I have a class final class CollectionViewBinder: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { }
that serves as a collection view binding helper for ReactiveCocoa.
The init method:
init(collectionView: UICollectionView, dataSignal: SignalProducer<[[Any]], NoError>, supplementarySignal: SignalProducer<[[Any]], NoError>?) { ... }
If the type of dataSignal is SignalProducer<[[String]], NoError>, that's when I get the error saying type SignalProducer<[[String]], NoError> can't be converted to type SignalProducer<[[Any]], NoError> which would lead me to think my error is something to do with ReactiveCocoa possibly?
You are right about Any, apple docs say:
Any can represent an instance of any type at all, including function types.
The example you gave works for me without any problems. Usually in this kind of situations the error is in some other place but the compiler gives a misleading message.
What you expect is called "generic covariance" and is currently not supported by Swift.
https://nomothetis.svbtle.com/type-variance-in-swift

Array of generics with metatypes (using AlamofireObjectMapper)

I suspect I may be making the same mistake as described by Rob in this post here in that I should be doing this whole thing another way, but with that in mind:
I'm trying to use AlamofireObjectMapper in a generic way. It has a protocol
public protocol Mappable
I then have various model classes that adopt it
class Dog: Mappable
class Cat: Mappable
class Bird: Mappable
I have this method
func loadEntityArray<T: Mappable>(type: T.Type)
and the reason this is generic is because it calls a function load() that needs a completion block that uses this generic param. The 'type' argument is never actually used, but you can't make a func generic without the generic type being in the func's parameter list.
func load(completion:(Response<T, NSError> -> Void))
loadEntityArray is called from another method
func letsgo() { loadEntityArray(Dog.self); loadEntityArray(Cat.self) }
So far so good, this all works. But I want to pass an array of which models to load to letsgo() and I can't work out how to do this. If I change letsgo() to
func letsgo<T:Mappable>(models: [T.Type]) {
for mod in models {
loadEntityArray(mod)
}
}
and then call letsgo() with 1 param like
letsgo([Dog.self])
it works, but as soon as I have an array of 2 or more, I get a compiler error 'cannot convert value of type NSArray to expected argument type [_.Type]' I don't now how I would explicitly type this array either.
letsgo([Dog.self, Cat.self])
I've tried various permutations and nothing seems to work. Am I doing something impossible here? It seems to me the compiler has enough information at compile time for this to work, so I'm not sure if this is a syntax thing or I'm doing something wrong here with generics.
Looking at your function :
func letsgo<T:Mappable>(models: [T.Type])
Its model parameter should be an Array of all the same Type. So an array of only Dog Types for example. That's why
letsgo([Dog.self])
works but
letsgo([Dog.self, Cat.self])
won't as it has multiple Types.
The solution :
Use Mappable.Type directly :
func loadEntityArray(type: Mappable.Type) {}
func letsgo(models: [Mappable.Type]) {
for mod in models {
loadEntityArray(mod.self)
}
}
And cast your array of Types as an array of Mappable Types :
letsgo([Dog.self, Cat.self] as [Mappable.Type])
Hope this achieves what you're looking for !
So in the end I came to the conclusion that this is not possible. I changed the code such that I pass in an enum to letsgo() and in a switch statement on that enum I call loadEntityArray(Dog.self) etc. etc. with an explicitly coded call, for each of my possible types. Then the compiler can see all the possible types and is happy.

Swift Extensions for Collections

I'm working on a framework to make it easier to work with Key Value Observing and I've defined a protocol for converting native Swift types to NSObject as follows:
public protocol NSObjectConvertible {
func toNSObject () -> NSObject
}
Extending the builtin types was easy, simply defining the function to convert the given type to the appropriate NSObject:
extension Int8: NSObjectConvertible {
public func toNSObject () -> NSObject {
return NSNumber(char: self)
}
}
When I got to the Array type, I hit a number of snags, which I tried to work out. I didn't want to extend any array type, but only arrays whose element type was itself NSObjectConvertible. And naturally, needed Array to itself conform to the protocol.
After hunting around on SO, it looks like extending the Array type itself is a little harder because it's generic, but extending SequenceType can be done. Except that I can't both constrain the element type and declare its conformance to the protocol in the same declaration.
The following:
extension SequenceType where Generator.Element == NSObjectConvertible : NSObjectConvertible = {
public func toNSObject () -> NSObject {
return self.map() { return $0.toNSObject() }
}
}
Produces a compiler error:
Expected '{' in extension
And the carat points to the ":" where I'm trying to declare the protocol conformance. Removing the protocol conformance compiles without errors, but obviously doesn't help the case.
I'm not sure if this is a bug, or if Swift simply can't (or doesn't want to) support what I'm trying to do. Even if I simply define the extension, then try to take care of the conformance in the body, it produces the risk of passing sequences that don't really conform to what they should.
At best it's a hacky solution to just fail in cases where a sequence with non-conforming members are passed. I'd much rather let the compiler prevent it from happening.
(This is in Swift 2.1, Xcode 7.1.1)
You can't add the protocol conformance, unfortunately.