guy
guy

Reputation: 619

Swift: Can I return a private type that implements a public class and conforms to a public protocol?

In Objective-C, a function is able to return instances of a private type that implements a public class and public protocol without ever defining a public class that conforms to that protocol.

E.g. Let's say I have this header file:

@protocol Flyer <NSObject>
-(void) fly;
@end

@interface Animal : NSObject
-(void) eat;
@end

Animal<Flyer> * randomFlyingAnimal();

And this implementation file:

@implementation Animal

-(void) eat {
    NSLog(@"I'm eating");
}

@end

@interface Bird : Animal<Flyer>
@end

@implementation Bird

-(void) fly {
    NSLog(@"I'm a flying bird");
}

@end

@interface Bat : Animal<Flyer>
@end

@implementation Bat

-(void) fly {
    NSLog(@"I'm a flying bat");
}

@end

Animal<Flyer> * randomFlyingAnimal() {
    switch (arc4random() % 2) {
        case 0:
            return [[Bird alloc] init];
        case 1:
        default:
            return [[Bat alloc] init];
    }
}

In this example, the consumer of my code never actually knows about the Bird class or Bat class (or any other type that implements Animal and conforms to Flyer), but can be sure that the object that's returned from randomFlyingAnimal can both eat and fly.

Is such a thing possible in Swift?

Upvotes: 1

Views: 304

Answers (1)

CouchDeveloper
CouchDeveloper

Reputation: 19106

There a a few approaches which you could apply in Swift, but you would probably define a protocol AnimalType and a protocol FlyerType - which do not have a relation to each other:

public protocol AnimalType {}
public protocol FlyerType {
    func fly()
}

Then, create internal or private classes as follows:

internal class Animal: AnimalType {}
internal class Bird: Animal {}
internal class Bat: Animal {}

Now, class Bird and Bat conforms to AnimalType through inheriting form its base class Animal. In order to conform to FlyerType as well we could extend these classes as follows:

extension Bird: FlyerType {
    internal func fly() { print("Bird's flying") }
}

extension Bat: FlyerType {
    internal func fly() { print("Bat's flying") }
}

Your factory function could then be implemented as below:

public func randomFlyingAnimal() -> protocol<AnimalType, FlyerType> {
    switch (arc4random() % 2) {
    case 0: return Bird()
    default: return Bat()
    }
}

protocol<AnimalType, FlyerType> is a protocol composition type - whose application seems useful in this case.

Upvotes: 0

Related Questions