Generic Types Collection - swift

Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
instantiate from protocol.Type reference dynamically at runtime
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}

Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.
At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.
This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:
When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)
You'll need to do whatever you're trying to do another way in Swift.
As a fun bonus, try explicitly asking for the impossible:
let thing = instantiate(Creatable.self)
And... swift crashes.
From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):
// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing"
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
func speak()
}
// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
func speak() {
println("Meow");
}
}
struct Dog : Speaking {
func speak() {
println("Woof");
}
}
// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
let name: String
func speak() {
println("My name is \(name)")
}
}
// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
let animal = builder()
animal.speak()
}
// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. #autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }
// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]
for animal in animalBuilders {
instantiateAndCallSpeak(animal)
}

Related

Generic constrained type default value

Consider the following code:
protocol JSONParserType {
associatedtype Element
}
// MARK: - Entities
struct Item {}
// MARK: - Parsers
struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
typealias Element = Item
let innerParser: T
init(innerParser: T = InnerParser()) {
self.innerParser = innerParser
}
}
struct InnerParser: JSONParserType {
typealias Element = Item
}
The OuterParser has a child parser that should be constrained to a specific type. Unfortunately providing a default value in the initializer (or in the property definition itself) does lead to the compiler throwing a "Default argument value of type 'InnerParser' cannot be converted to type 'T'".
If I remove the default value assignment and just instantiate the OuterParser providing the InnerParser explicitly, everything is fine.
let outerParser = OuterParser(innerParser: InnerParser())
My question is what's the reason that the approach providing a default value that actually meets the constraints does not work.
The problem is that the actual type of T isn't defined by the class – it's defined by the code that uses the class. It will therefore be defined before you do anything in your class (at either instance or static level). You therefore can't assign InnerParser to T, as T has already been defined to be a given type by that point, which may well not be InnerParser.
For example, let's consider that you have another parser struct:
struct AnotherParser: JSONParserType {
typealias Element = Item
}
and let's assume that your current code compiles. Now consider what would happen when you do this:
let parser = OuterParser<AnotherParser>()
You've defined the generic type to be AnotherParser – but the initialiser will try to assign InnerParser to your property (now of type AnotherParser). These types don't match, therefore it cannot possibly work.
Following the same logic, this implementation also won't work:
struct OuterParser<T: JSONParserType where T.Element == Item>: JSONParserType {
typealias Element = Item
let innerParser: T
init() {
self.innerParser = InnerParser()
}
init(innerParser: T) {
self.innerParser = innerParser
}
}
As there's no guarantee that the generic type T will be the same type as InnerParser. Sure, you can force downcast to T – but that'll just make you code crash if the types aren't compatible.
Unfortunately, there's no real clean solution to this problem. I think the best your best option is probably to create two factory methods for creating your OuterParser instance.
enum Parser {
static func createParser() -> OuterParser<InnerParser> {
return OuterParser(innerParser:InnerParser())
}
static func createParser<T>(innerParser:T) -> OuterParser<T> {
return OuterParser(innerParser:innerParser)
}
}
let innerParser = Parser.createParser() // OuterParser<InnerParser>
let anotherParser = Parser.createParser(AnotherParser()) // OuterParser<AnotherParser>
We're using an caseless enum here to avoid polluting the global namespace with extra functions.
Although this isn't very Swifty, and for that reason I would also recommend maybe rethinking your logic for how you define your parsers.
type T more like a child protocol of JSONParserType you can convert it:
init(innerParser: T = InnerParser() as! T) {
self.innerParser = innerParser
}

Constraining one generic with another in Swift

I've run into a problem where I have some protocol:
protocol Baz {
func bar<T>(input:T)
}
The function bar is made generic because I don't want the protocol itself to have a Self(it needs to be useable in a collection). I have an implementation of the protocol defined as:
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
value = input
}
}
This gives an error because the compiler doesn't know that S and T are the same type. Ideally I should be able to write something like:
func bar<T where T==S>(input:T) {
value = input
}
or
func bar<T:S>(input:T) {
value = input
}
The first form gives a "Same-type requirement makes generic parameter 'S' and 'T' equivalent" error (which is exactly what I'm trying to do, so not sure why it's an error). The second form gives me a "Inheritance from non-protocol, non-class type 'S'".
Any ideas of on either how to get this to work, or a better design pattern in Swift?
Update: As #luk2302 pointed out, I forgot to make Foo adhere to the Baz protocol
#luk2302 has hinted at much of this in the comments, but just to make it explicit for future searchers.
protocol Baz {
func bar<T>(input:T)
}
This protocol is almost certainly useless as written. It is effectively identical to the following protocol (which is also almost completely useless):
protocol Baz {
func bar(input:Any)
}
You very likely mean (and hint that you mean):
protocol Baz {
typealias T
func bar(input: T)
}
As you note, this makes the protocol a PAT (protocol with associated type), which means you cannot put it directly into a collection. As you note, the usual solution to that, if you really need a collection of them, is a type eraser. It would be nice if Swift would automatically write the eraser for you, which it likely will be able to do in the future, and would eliminate the problem. That said, though slightly tedious, writing type erasers is very straightforward.
Now while you cannot put a PAT directly into a collection, you can put a generically-constrained PAT into a collection. So as long as you wrap the collection into a type that constrains T, it's still no problem.
If these become complex, the constraint code can become tedious and very repetitive. This can be improved through a number of techniques, however.
Generic structs with static methods can be used to avoid repeatedly providing constraints on free-functions.
The protocol can be converted into a generic struct (this formalizes the type eraser as the primary type rather than "as needed").
Protocols can be replaced with functions in many cases. For example, given this:
protocol Bar {
typealias T
func bar(input: T)
}
struct Foo : Bar {
func bar(input: Int) {}
}
You can't do this:
let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
But you can easily do this, which is just as good:
let bars = [(Int) -> Void] = [Foo().bar]
This is particularly powerful for single-method protocols.
A mix of protocols, generics, and functions is much more powerful than trying to force everything into the protocol box, at least until protocols add a few more missing features to fulfill their promise.
(It would be easier to give specific advice to a specific problem. There is no one answer that solves all issues.)
EDITED (Workaround for "... an error because the compiler doesn't know that S and T are the same type.")
First of all: This is just an separate note (and perhaps an attempt at redemption for my previous answer that ended up being myself chasing my own tail to compute lots and lots of redundant code) in addition to Robs excellent answer.
The following workaround will possibly let your implementation protocol Foo ... / class Bas : Foo ... mimic the behaviour you initially asked for, in the sense that class method bar(...) will know if generics S and T are actually the same type, while Foo still conforms to the protocol also in the case where S is not of the same type as T.
protocol Baz {
func bar<T>(input:T)
}
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0
// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0
The Any and AnyObject loophole here could be redeemed by creating a dummy type constraint that you extend all types (that you wish for to use the generics) to, however not extending Any and AnyObject.
protocol NotAnyType {}
extension Int : NotAnyType {}
extension Double : NotAnyType {}
extension Optional : NotAnyType {}
// ...
protocol Baz {
func bar<T: NotAnyType>(input:T)
}
class Foo<S: NotAnyType>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T: NotAnyType>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
// ...
// no longer a loophole
let myAny : Any = 3.0
a.bar(myAny) // compile time error
let myAnyObject : AnyObject = 3.0
a.bar(myAnyObject) // compile time error
This, however, excludes Any and AnyObject from the generic in full (not only for "loophole casting"), which is perhaps not a sought after behaviour.

Swift 2: understanding AnyObject and Self

I couldn't find any good explanation to my questions so I'd like to ask you directly. First of all I'd like to refine my code in this post.
My problem is the protocol AnyObject and the Self type. I didn't implement AnyObject into my code because it is marked with #objc and I don't want any Objective-C stuff involved in my code (don't judge me for that). I also couldn't find any explanation about the Self type. It just worked as expected, but Xcode does not replace Self with the type the static function is called at.
Here is some example:
extension Int : Instance {}
Int.singleton { (customInstanceName) -> Self in 0 } // Self shall be replaced with Int
As you can see Xcode produces a Self instead an Int. Is there any chance I could fix this? Am I right that Self does return the dynamicType and my implementation is fine as it is in my post above? I would really appreciate any good explanation about the Self type.
As you have seen in my code. I am using a custom protocol to check whether my instance is a class or not. Is there any other shiny implementation to check my instances if they are classes or structure types, or am I forced to use AnyObject if I want to get rid of my ClassInstance protocol?
Thank you for your time.
UPDATE:
protocol Test {}
class A : Test {}
struct B : Test {}
let aClass : Test = A()
let aStruct : Test = B()
if let someClass = aClass as? AnyObject {
print(someClass) // only this will print
}
if let someStruct = aStruct as? AnyObject {
print(someStruct)
}
This will work, but AnyObject is still marked as an #objc protocol.
The Self type can be only used in protocols where it is a implicit typealias of the type which conforms to it:
protocol Testable {
func test() -> Self
}
If you want to conform to this protocol you than have to replace Self with the name of the type. For instance:
struct Product: Testable {
func test() -> Product {
return Product()
}
}
Important Edit:
As DevAndArtist pointed out in the comments there is a working class check in Swift 1.2 (without automatic bridging to Objective C) but not Swift 2 (Xcode 7 beta 3; probably a bug):
if instance.dynamicType is AnyClass {
// instance is a class
} else {
// instance is not a class
}
You can see workaround (mainly) for Swift 2 below.
End Edit
With respect to classes you should use AnyObject if you want to keep it simple but you can also use reflection which would be much more effort.
Below you can see some reflection results of string interpolations (only the first few characters):
"\(reflect(classType))" // Swift._ClassMirror
"\(reflect(0))" // Swift._LeafMirror
"\(reflect(enumType))" // Swift._EnumMirror
"\(reflect(structure))" // Swift._StructMirror
"\(reflect([0, 4]))" // Swift._ArrayTypeMirror
"\(reflect(NSDate()))" // Foundation._NSDateMirror
"\(reflect(NSURLRelationship.Contains))" // Swift._EnumMirror
"\(reflect(Int?(2)))" // Swift._OptionalMirror
As you can see enums are consistent if they are not defined in the Swift standard library (unfortunately also Optional...). So you can distinguish also structs and enums:
public enum Type {
case Enum, Class, Struct
}
public func getType<T>(anything: T) -> Type {
if anything is AnyObject {
return .Class
}
if "\(reflect(anything))".hasPrefix("Swift._EnumMirror") {
return .Enum
}
return .Struct
}
So for a better result you have to put some effort into it to differentiate between all the different cases.
But the easiest way to distinguish only between reference types and value types (aka classes and structs/enums) is still (unfortunately only works for own declared structs and not built in types because they can be bridged to Objective C; I'm working on it...):
if instance is AnyObject {}
// or: if instance is of type Any
if let classInstance = instance as? AnyObject {}

Swift: Protocol Based Type Construction

I'm trying to create a protocol in Swift I can use for object construction. The problem I'm running into is that I need to store the type information so the type can be constructed later and returned in a callback. I can't seem to find a way to store it without either crashing the compiler or creating build errors. Here's the basics (a contrived, but working example):
protocol Model {
init(values: [String])
func printValues()
}
struct Request<T:Model> {
let returnType:T.Type
let callback:T -> ()
}
We have a simple protocol that declares a init (for construction) and another func printValues() (for testing). We also define a struct we can use to store the type information and a callback to return the new type when its constructed.
Next we create a constructor:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
A couple things to note: This causes a compiler crash. It can't figure this out for some reason. The problem appears to be var callbacks: [Request<Model>] = []. If I comment out everything else, the compiler still crashes. Commenting out the var callbacks and the compiler stops crashing.
Also, the func construct works fine. But it doesn't store the type information so it's not so useful to me. I put in there for demonstration.
I found I could prevent the compiler from crashing if I remove the protocol requirement from the Request struct: struct Request<T>. In this case everything works and compiles but I still need to comment out let model = request.returnType(values: ["value1", "value2"]) in func next(). That is also causing a compiler crash.
Here's a usage example:
func construct() {
let constructor = Constructor()
let request = Request(returnType: TypeA.self) { req in req.printValues() }
//This works fine
constructor.construct(TypeA.self) { a in
a.printValues()
}
//This is what I want
constructor.queueRequest(request)
constructor.next() //The callback in the request object should be called and the values should print
}
Does anyone know how I can store type information restricted to a specific protocol to the type can later be constructed dynamically and returned in a callback?
If you want the exact same behavior of next I would suggest to do this:
class Constructor {
// store closures
var callbacks: [[String] -> ()] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
// some code from the next function so you don't need to store the generic type itself
// **EDIT** changed closure to type [String] -> () in order to call it with different values
callbacks.append({ values in
let model = request.returnType(values: values)
request.callback(model)
})
}
func next(values: [String]) {
callbacks.first?(values)
}
}
Now you can call next with your values. Hopefully this works for you.
EDIT: Made some changes to the closure type and the next function
Unfortunately there is no way to save specific generic types in an array and dynamically call their methods because Swift is a static typed language (and Array has to have unambiguous types).
But hopefully we can express something like this in the future like so:
var callbacks: [Request<T: Model>] = []
Where T could be anything but has to conform to Model for example.
Your queueRequest method shouldn't have to know the generic type the Request it's being passed. Since callbacks is an array of Request<Model> types, the method just needs to know that the request being queued is of the type Request<Model>. It doesn't matter what the generic type is.
This code builds for me in a Playground:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest(request: Request<Model>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
So I found an answer that seems to do exactly what I want. I haven't confirmed this works yet in live code, but it does compile without any errors. Turns out, I needed to add one more level of redirection:
I create another protocol explicitly for object construction:
protocol ModelConstructor {
func constructWith(values:[String])
}
In my Request struct, I conform to this protocol:
struct Request<T:Model> : ModelConstructor {
let returnType:T.Type
let callback:T -> ()
func constructWith(values:[String]) {
let model = returnType(values: values)
callback(model)
}
}
Notice the actual construction is moved into the Request struct. Technically, the Constructor is no longer constructing, but for now I leave its name alone. I can now store the Request struct as ModelConstructor and correctly queue Requests:
class Constructor {
var callbacks: [ModelConstructor] = []
func queueRequest(request: Request<Model>) {
queueRequest(request)
}
func queueRequest(request: ModelConstructor) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
request.constructWith(["value1", "value2"])
callbacks.removeAtIndex(0)
}
}
}
Note something special here: I can now successfully "queue" (or store in an array) Request<Model>, but I must do so indirectly by calling queueRequest(request: ModelConstructor). In this case, I'm overloading but that's not necessary. What matters here is that if I try to call callbacks.append(request) in the queueRequest(request: Request<Model>) function, the Swift compiler crashes. Apparently we need to hold the compiler's hand here a little so it can understand what exactly we want.
What I've found is that you cannot separate Type information from Type Construction. It needs to be all in the same place (in this case it's the Request struct). But so long as you keep construction coupled with the Type information, you're free to delay/store the construction until you have the information you need to actually construct the object.

Determining protocol types at runtime

How can we determine if protocol conforms to a specific subtype based on user provided instances, if it's not possible this way, any alternate solutions.
API
protocol Super {}
protocol Sub: Super {} //inherited by Super protocol
class Type1: Super {} //conforms to super protocol
class Type2: Type1, Sub {} //conforms to sub protocol
inside another API class
func store(closures: [() -> Super]) {
self.closures = closures
}
when it's time to call
func go() {
for closure in closures {
var instance = closure()
if instance is Super {
//do something - system will behave differently
} else { //it's Sub
//do something else - system will behave differently
}
}
}
users of the api
class Imp1: Type1 {}
class Imp2: Type2 {}
var closures: [() -> Super] = [ { Imp1() }, { Imp2() } ]
store(closures)
my current workaround within API
func go() {
for closure in closures {
var instance = closure()
var behavior = 0
if instance as? Type2 != nil { //not so cool, should be through protocols
behavior = 1 //instead of implementations
}
if behavior == 0 { //do something within the api,
} else { //do something else within the api
}
//instance overriden method will be called
//but not important here to show, polymorphism works in here
//more concerned how the api can do something different based on the types
}
}
You are jumping through a lot of hoops to manually recreate dynamic dispatch, i.e. one of the purposes of protocols and classes. Try actually using real runtime polymorphism to solve your problem.
Take this code:
if instance is Super {
//do something
} else { //it's Sub
//do something else
}
What you are saying is, if it’s a superclass, run the superclass method, else, run the subclass. This is a bit inverted – normally when you are a subclass you want to run the subclass code not the other way around. But assuming you turn it around to the more conventional order, you are essentially describing calling a protocol’s method and expecting the appropriate implementation to get called:
(the closures aren’t really related to the question in hand so ignoring them for now)
protocol Super { func doThing() }
protocol Sub: Super { } // super is actually a bit redundant here
class Type1: Super {
func doThing() {
println("I did a super thing!")
}
}
class Type2: Sub {
func doThing() {
println("I did a sub thing!")
}
}
func doSomething(s: Super) {
s.doThing()
}
let c: [Super] = [Type1(), Type2()]
for t in c {
doSomething(t)
}
// prints “I did a super thing!”, then “I did a sub thing!"
Alternatives to consider: eliminate Sub, and have Type2 inherit from Type1. Or, since there’s no class inheritance here, you could use structs rather than classes.
Almost any time you find yourself wanting to use is?, you probably meant to use an enum. Enums allow you use to the equivalent of is? without feeling bad about it (because it isn't a problem). The reason that is? is bad OO design is that it creates a function that is closed to subtyping, while OOP itself is always open to subtyping (you should think of final as a compiler optimization, not as a fundamental part of types).
Being closed to subtyping is not a problem or a bad thing. It just requires thinking in a functional paradigm rather than an object paradigm. Enums (which are the Swift implementation of a Sum type) are exactly the tool for this, and are very often a better tool than subclassing.
enum Thing {
case Type1(... some data object(s) ...)
case Type2(... some data object(s) ...)
}
Now in go(), instead of an is? check, you switch. Not only is this not a bad thing, it's required and fully type-checked by the compiler.
(Example removes the lazy closures since they're not really part of the question.)
func go(instances: [Thing]) {
for instance in instances {
switch instance {
case Type1(let ...) { ...Type1 behaviors... }
case Type2(let ...) { ...Type2 behaviors... }
}
}
}
If you have some shared behaviors, just pull those out into a function. You're free to let your "data objects" implement certain protocols or be of specific classes if that makes things easier to pass along to shared functions. It's fine if Type2 takes associated data that happens to be a subclass of Type1.
If you come along later and add a Type3, then the compiler will warn you about every switch that fails to consider this. That's why enums are safe while is? is not.
You need objects derived from the Objective-C world to do this:
#objc protocol Super {}
#objc protocol Sub: Super {}
class Parent: NSObject, Super {}
class Child: NSObject, Sub {}
func go( closures: [() -> Super]) {
for closure in closures {
let instance = closure()
if instance is Sub { // check for Sub first, check for Super is always true
//do something
} else {
//do something else
}
}
}
Edit: Version with different method implementations:
protocol Super {
func doSomething()
}
protocol Sub: Super {}
class Parent: Super {
func doSomething() {
// do something
}
}
class Child: Sub {
func doSomething() {
// do something else
}
}
func go( closures: [() -> Super]) {
for closure in closures {
let instance = closure()
instance.doSomething()
}
}