Reputation: 1363
I came across this problem which I wasn't able to solve and haven't found much about it.
My case is that from a factory function I want to return an UIViewController
instance which also implements a protocol let's call it Protocol
. I'd like to know if anyone experienced this and found any solution?
In Objective-C it would look like this:
- (UIViewController<Protocol>*)createControllerForIdentifier:(NSString*)identifier
Is there any Swift way to write this?
Restricting Protocol
to concrete class or subclass would be OK for my case.
I've found this thread but wasn't able to convert to my case
Thanks for your help.
Upvotes: 3
Views: 1260
Reputation: 1363
Swift 4 ❤️
func createController(for identifier: String) -> (UIViewController & Protocol)
Upvotes: 1
Reputation: 1285
There are several options depending on the specifics. Here is one general approach that may make help. However, I recommend rewriting the code so this isn't really an issue. Swift and Objective-C are different languages and some design patterns are simply not (and may never be) available to both languages. In such cases, porting code requires rethinking the code from scratch.
You can achieve both safety and convenience by introducing a binding protocol as follows:
// protocol you want to implement
// this may possibly be implemented by other types besides our Base (no restrictions)
protocol P1 {
func run()
}
// base class of objects to instantiate
class Base {
// Base specific method
func display() {
print("Displaying...")
}
}
// wrapper to get both P1 and Base
// our special protocol will be dedicated to P1 and Base
protocol PB : P1 { // already a P1
// property to safely get this instance as a Base without casting
var asBase : Base { get }
}
// extension to safely implement the binding for P1 and Base
// anything we implement in this extension is safe for both P1 and Base
extension PB where Self : Base {
var asBase : Base { return self }
}
// concrete subclass of Base which also implements PB and hence P1
class X : Base, PB {
// implement protocol
func run() {
print("Running...")
}
}
// factory function to make a concrete instance of Base implementing P1
func makePB() -> PB {
return X()
}
let pb = makePB()
pb.run() // directly access as P1
pb.asBase.display() // both safe and easy to access as Base
Upvotes: 1
Reputation: 73176
One method is as follows:
protocol Protocol {
func bar()
}
class MyViewController : UIViewController, Protocol {
func bar() {
print("Bar")
}
static func getInstance() -> MyViewController {
return self.init()
}
}
/* Example usage */
let foo = MyViewController.getInstance()
print(foo.dynamicType) // MyViewController
foo.bar() // Bar
/* Naturally works to pass foo to 'Protocol' type constrained function */
func foobar<T: Protocol>(fizz: T) {
fizz.bar()
}
foobar(foo) // bar
Or, for a more generic/re-usable approach (factory method for classes/structures that have a simple init()
initializer available):
/* Setup generic factory */
protocol FactoryInitializers {
init()
}
protocol FactoryMethods {
typealias T: FactoryInitializers
}
extension FactoryMethods {
static func getInstance() -> T {
return T()
}
}
protocol Factory : FactoryMethods, FactoryInitializers { }
/* Apply for your example, with conformance to 'Factory' (to get access
to default implementation 'getInstance' method) as well as a custom
protocol 'Protocol' */
protocol Protocol {
func bar()
}
class MyViewController : UIViewController, Factory, Protocol {
typealias T = MyViewController
func bar() {
print("Bar")
}
}
With the same result as the simpler version above:
let foo = MyViewController.getInstance() // OK, MyViewController conforms to Factory
print(foo.dynamicType) // MyViewController
foo.bar() // Bar
/* Naturally works to pass foo to 'Protocol' type constrained function */
func foobar<T: Protocol>(fizz: T) {
fizz.bar()
}
foobar(foo) // bar
Upvotes: 0