Declare array of classes that conform to a protocol
"array of classes that conform to a protocol" can be declared like Array<TheProtocol.Type>
.
You can:
var array: Array<ControllerConstructorProtocol.Type> = [
MyConstructor.self,
MyOtherConstructor.self,
]
But...,
array[0].construct()
// ^ error: accessing members of protocol type value 'ControllerConstructorProtocol.Type' is unimplemented
Calling method on the item is "unimplemented".
As of now, you have to declare the protocol as @objc
, and call the method via AnyClass
. Moreover, for some reasons, we cannot directly cast array[0]
to AnyClass
, instead, we have to cast it to Any
, then AnyClass
.
@objc protocol ControllerConstructorProtocol {
class func construct() -> UIViewController?
}
var array: Array<ControllerConstructorProtocol.Type> = [
MyConstructor.self,
MyOtherConstructor.self,
]
let vc = (array[0] as Any as AnyClass).construct()
Note: Casting problem was fixed in Swift 1.2 / Xcode 6.3. But "unimplemented" is "unimplmented" :(
Just random ideas:
It's depends on your actual use-case, but in this particular case, array of ()-> UIViewController?
closures is sufficient:
var array: [() -> UIViewController?] = [
MyConstructor.construct,
MyOtherConstructor.construct,
]
let vc = array[0]()
If you have several methods, you might want to use type-erased wrapper of the protocol.
protocol ControllerConstructorProtocol {
class func construct() -> UIViewController?
class func whoami() -> String
}
struct ControllerConstructorWrapper {
private let _construct: () -> UIViewController?
private let _whoami: () -> String
init<T: ControllerConstructorProtocol>(_ t:T.Type) {
_construct = { t.construct() }
_whoami = { t.whoami() }
}
func construct() -> UIViewController? { return _construct() }
func whoami() -> String { return _whoami() }
}
var array: [ControllerConstructorWrapper] = [
ControllerConstructorWrapper(MyConstructor),
ControllerConstructorWrapper(MyOtherConstructor),
]
let who = array[0].whoami()
let vc = array[0].construct()
How to define an array of objects conforming to a protocol?
The problem is about using the generics counterpart for protocols, type aliases.
It sounds weird, but if you define a type alias, you cannot use the protocol as a type, which means you cannot declare a variable of that protocol type, a function parameter, etc. And you cannot use it as the generic object of an array.
As the error say, the only usage you can make of it is as a generic constraint (like in class Test<T:ProtocolWithAlias>
).
To prove that, just remove the typealias from your protocol (note, this is just to prove, it's not a solution):
protocol MyProtocol {
var abc: Int { get }
}
and modify the rest of your sample code accordingly:
class XYZ: MyProtocol {
var abc: Int { return 32 }
}
var list = [MyProtocol]()
You'll notice that it works.
You are probably more interested in how to solve this problem. I can't think of any elegant solution, just the following 2:
- remove the typealias from the protocol and replace
T
withAnyObject
(ugly solution!!) - turn the protocol into a class (but that's not a solution that works in all cases)
but as you may argue, I don't like any of them. The only suggestion I can provide is to rethink of your design and figure out if you can use a different way (i.e. not using typealiased protocol) to achieve the same result.
Pass an array of protocol conforming objects to function
As others have said you want to have func execute(requests: [Request]) {}
in this case.
The reasoning is that func execute<T: Request>(requests: [T]) {}
as a generic function is saying you want a concrete known type T
that conforms to the Request
protocol.
The change to [Request]
from [T]
lets you have an array of any type that conforms to Request
instead of an array of one type that conforms.
Swift Array of Class Types that Each Conform to a Protocol and are Subclasses of Common Class
You can achieve that by having SomeClass
conform to MyProtocol
. Therefore every subclass would also have to conform to MyProtocol
.
Then your method signature would be just:
func myMethod<T>(class: T.Type) where T: SomeClass
If that is not desired then you can change the type of myMethod to not be generic. I think swift is unable to type the array to have elements of (SomeClass & MyProtocol).Type. In your case you are not really using a generic object. You do have a specific type in mind and it is (SomeClass & MyProtocol).Type
. Your code would look something like this:
protocol MyProtocol: class {
}
class SomeClass: NSObject {
}
class SubClass: SomeClass, MyProtocol {
}
class SubClass2: SomeClass, MyProtocol {
}
let classArray: [(SomeClass & MyProtocol).Type] = [SubClass.self, SubClass2.self]
func myMethod(class: (SomeClass & MyProtocol).Type) {
}
classArray.forEach {
myMethod(class: $0)
}
Can't create an Array of types conforming to a Protocol in Swift
Let's say, if we could put an instance of Thing
into array foos
, what will happen?
protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}
class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}
class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}
var foos: [Foo] = [Thing()]
Because AnotherThing
conforms to Foo
too, so we can put it into foos
also.
foos.append(AnotherThing())
Now we grab a foo
from foos
randomly.
let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]
and I'm going to call method bar
, can you tell me that I should send a string or an integer to bar
?
foo.bar("foo")
or foo.bar(1)
Swift can't.
So it can only be used as a generic constraint.
What scenario requires a protocol like this?
Example:
class MyClass<T: Foo> {
let fooThing: T?
init(fooThing: T? = nil) {
self.fooThing = fooThing
}
func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine
let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it
// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}
How to make a swift class conform to a protocol that requires a protocol-conform type for one of its properties?
You can do this with associated types.
Declare an associated type in ProtocolA
:
protocol ProtocolA {
associatedtype BType: ProtocolB
var b: BType? { get } // Note the change in the type of b
}
This is saying that b
is an optional of some type that conforms to ProtocolB
. What type exactly? That depends on the conformers of ProtocolA
.
In A
's extension, you specify what BType
is:
extension A: ProtocolA {
typealias BType = B
}
And you're done!
As a result of this, you won't be able to use ProtocolA
as the type of a variable:
var protocolA: ProtocolA? // error
Because you don't know what b
's type is.
Swift: Array property with elements conforming to a class and multiple protocols simultaneously
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()]
Related Topics
Using Applescript with Apple Events in MACos - Script Not Working
What Would Be a Proper Storyboard Example of Combining Nav Bars and Tab Bars in One App
Array as a Dictionary Value in Swift Language
How to Access Firebase Variable Outside Firebase Function
Any Way to Chain == and || Operands
Change Time Interval in Skaction.Waitforduration() as Game Goes On
How to Make Swiftui Listmenu with Different Behaviors
How to Get the Current Queue Name in Swift 3
Fastlane "Nokogiri Requires Ruby Version >= 2.3.0." Error
Realitykit - How to Set a Modelentity's Transparency
How to Embed Third Party Framework on Ionic Capacitor Custom Plugin
Executing Text-To-Speech in Order
Swift: Providing a Default Protocol Implementation in a Protocol Extension
Is There a Preferred Technique to Prohibit Pasting into a Uitextfield