olejnjak
olejnjak

Reputation: 1363

Return object of concrete class that implements a protocol

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

Answers (3)

olejnjak
olejnjak

Reputation: 1363

Swift 4 ❤️

func createController(for identifier: String) -> (UIViewController & Protocol)

Upvotes: 1

Tom Pelaia
Tom Pelaia

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

dfrib
dfrib

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

Related Questions