6

In Objective-C there is a way to declare a variable conforming to a class and a set of protocols like this:

BaseClass<Protocol1, Protocol2> *variable = ...

In Swift I would like to declare an array (actually as a property of a class) with elements of a type defined by this pattern.

In this question there is a solution for describing the type of a standalone property by making the class generic and constraining the type accordingly. In order to instantiate such a class it would be necessary to specify the exact type. This is not a problem for a standalone property, but in an array there should be possible to store elements with different exact types.

Is there a way how to express this in Swift?

4
  • Not sure if I got you right, but if the array elements are all the same type then use [<type>]() to instantiate it. Commented Jan 19, 2015 at 14:37
  • @ThomasKilian No, they are not. That's exactly the point. Commented Jan 19, 2015 at 14:41
  • @LukasKubanek is it required, that only objects conforming to these protocols are stored in the array? Commented Jan 19, 2015 at 14:45
  • @SebastianDressler Yes. And not only the protocols but also the base class. Additionally I'd like to make it as type safe as possible. Commented Jan 19, 2015 at 14:49

3 Answers 3

4

Thanks to @SebastianDressler and @Mike-S I found out that there isn't a straightforward way how to express this in Swift.

What you can do though is to constrain the type of the item you want to add to the array like this:

func addItem<T where T: BaseClass, T: Protocol1, T: Protocol2>(item: T) {
    self.array.append(item)
}

Casting the items from the array when its type is defined as the one of the base class of the protocols is not possible, because the compiler doesn't see any relation between those types.

In order to be able to cast to the base class or to one of the protocols, it is necessary to declare the type as AnyObject.

var array: [AnyObject] = []

And casting to the protocols only works when they are annotated with @objc (see https://stackoverflow.com/a/24318145/670119).

Sign up to request clarification or add additional context in comments.

Comments

1

In the case, that you want to store only objects conforming to your protocols, you can make another protocol which inherits the others, e.g.

protocol A { }
protocol B { }
protocol C : A, B { }

Now you can create the corresponding array

var objects : [ C ]

You can store any object, as long as it conforms to the C-protocol and thus to A and B as well:

class Foo : X { }
class Bar : X { }

objects.append(Foo()) // [ Foo ]
objects.append(Bar()) // [ Foo, Bar ]

The technique behind is Protocol Inheritance.

Update IMO this is not feasible with the Array of Swift. Because, you can either store a type inherited from a base class or AnyObject, which does not satisfy your constraints. But you could possibly create a wrapper which checks the object you try to append to your array and rejects it if it does not fit the constraints.

4 Comments

What I need is also a conformance to a base class. For composing the protocols I also could define the type using protocol<A, B>, but I am looking for a way to combine it with a class type.
Sorry, but then I quite don't get the point. If there is a Foo conforming to protocols A and B, and you want to store objects wrt Foo and thus A and B into an array, then these objects must inherit from Foo again. Otherwise there is no guarantee that it satisfies these constraints.
Maybe I wasn't too clear in my answer, but Foo doesn't inherit from the protocols. It's just an additional type requirement - same as in the Obj-C declaration above.
Thanks for the update and your help! I actually did it by using a generic wrapper function which checks the requirements.
1

I just came across this old question of mine and because the Swift language evolved since accepting the partial answer I decided to post another answer which actually solves the problem I originally asked.

From version 4, Swift supports protocol composition using the & sign which can also be composed with one class type.

class BaseClass {}
protocol Protocol1 {}
protocol Protocol2 {}

class ConformingClass1: BaseClass, Protocol1, Protocol2 {}
class ConformingClass2: BaseClass, Protocol1, Protocol2 {}

// It works for a variable holding a single object.
let object: BaseClass & Protocol1 & Protocol2 = ConformingClass1()

// And it also works for a variable holding an array of objects.
let array: [BaseClass & Protocol1 & Protocol2] = [ConformingClass1(), ConformingClass2()]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.