Štěpán
Štěpán

Reputation: 348

Swift: func return type inheritting class and conforming one or more protocols

From an external library I've got the following scenario.

The protocol and base class:

protocol P1 {
    // P1 Stuff
}

class A {
    // A Stuff
}

Then there is an extension, which causes my troubles. It is defined for the combination of types P1 and A.

extension P1 where Self : A {
    public func myDesiredFunc() {
        // Do stuff.
    }
}

And finally there are the implementations B1 - Bn, which I use.

class B1 : A, P1 {
}
class B2 : A, P1 {
}
...

In my code I need to put together instances of B-classes and work with them. The problem is, that I need to use the extension func myDesiredFunc(). So I need somehow define, that the array type is something like [(A & P1)] and the return type of producing funciton is (A & P1) as well.

However, with this code:

func createBeeInstance() -> (A & P1) {
    if something {
        return B1()
    } else if something {
        return B2()
    }
    ...
}

I'm getting the error:

Non-protocol type 'A' cannot be used within a protocol composition.

Is there a way to say that the return type is a composition of class and protocol? I'm not able to change the scenario because it's an external library.

Upvotes: 1

Views: 1219

Answers (2)

jlehr
jlehr

Reputation: 15597

One possible solution would be to define a protocol that defines the essential nature of objects of type A (let's call the new protocol Aable), and make the A type conform to it:

class A: Aable {
    // A stuff
}

You could then constrain the P1 protocol extension with Aable instead of A:

extension P1 where Self : Aable {
    func myDesiredFunc() {
        // Do stuff.
    }
}

That would allow you to use protocol composition for the return type of a function...

func createBeeInstance(useB1: Bool) -> (Aable & P1) {
    return useB1 ? B1() : B2()
}

...as well as for the element type of an array:

var things = [Aable & P1]()

for i in 1...5 {
    let b = createBeeInstance(useB1: i % 2 == 0)
    things.append(b)
}

for thing in things {
    thing.myDesiredFunc()
}

Upvotes: 1

M.Othman
M.Othman

Reputation: 5300

Swift 4:

^_^
it's possible now to compose class and protocol in swift 4 so modifying the previous snippets to this should work

class A {
// A Stuff
required init () {}
}
protocol P1 {
// P1 Stuff
}

class B1 : A {}
class B3: P1 {}
class B2 : A, P1 {}

func createBeeInstance<T: P1 & A>(type: T.Type) -> T  {
return type.init()
 }

var things = [P1 & A]() // still Element has to be P1 as well
let f = createBeeInstance(type: B2.self)
//let f1 = createBeeInstance(type: B1.self) // will error
//let f2 = createBeeInstance(type: B3.self) // will error

things.append(f) // is fine

--- OLD Way which did not work ---

You may use this way , having the Sample you provided with modifying class A to have init

class A {
    // A Stuff
    required init () {}
}

modifying createBee method to

func createBeeInstance<T: P1>(type: T.Type) -> T where T: A {
    return type.init()
}

this way you will provide the type as an input e.g. B1.self

for array we can provide typealieased generic

typealias B<T:P1> = T where T: A

var things = [B<A>]() // still Element has to be P1 as well 
let f = createBeeInstance(type: B2.self)
let f1 = createBeeInstance(type: B1.self)
things.append(f)

Upvotes: 1

Related Questions