3

I'm trying to build a set of classes that would share common initializing code. Apart from inheritance, I thought protocols would be the way to go. While protocols and protocol extensions worked for me with instance and static methods, I have some troubles making it work with initializers.

Let's say we have this protocol:

protocol CloudServiceWrapper {

    static var isConfigured: Bool { get }

    init()

    func initialize()

}

Now let's say we want to add default implementations for both isConfigured and init() in a protocol extension:

extension CloudServiceWrapper {

    static var isConfigured: Bool {
        get {
            return true
        }
    }

    init() {
        print("Initializing generic CloudServiceWrapper")
        self.init()
        if Self.isConfigured {
            initialize()
        }

    }

}

Finally, let's have a class implementing this protocol and trying to benefit from its default implementations:

class OneDriveWrapper: CloudServiceWrapper {

    required init() {
        // CloudServiceWrapper() // error: protocol type 'CloudServiceWrapper' cannot be instantiated
        // self = CloudServiceWrapper.init() // error: cannot assign to value: 'self' is immutable
        // super.init() // error: 'super' members cannot be referenced in a root class
        // (self as CloudServiceWrapper).init() // error: static member 'init' cannot be used on instance of type 'CloudServiceWrapper'
        print("Initializing OneDriveWrapper")
    }

    func initialize() {
        print("Done")
    }

}

When trying to build a new instance of the OneDriveWrapper class, there's simply no way I could find to both call the class' initializer, and the default protocol implementation. And it is not possible to omit the init() in the OneDriveWrapper class, since it is required per the protocol definition, and seemingly not considered "implemented" with the protocol extension.

In fact, more broadly, I couldn't find any way to explicitly call a protocol extension's initializer, even though I know it's possible for instance methods.

What am I doing wrong? Do you know any way of combining both the class' initialiser and the protocol extension's one? Should I fall back to class inheritance instead of protocols and extensions?

Thanks!

Craig Siemens
  • 12,942
  • 1
  • 34
  • 51
Romain
  • 3,718
  • 3
  • 32
  • 48

3 Answers3

5

An init within a protocol is required, and therefore has to be implemented explicitly i.e. a default implementation cannot be utilised.

As for 'explicitly calling a protocol extension's initializer', you cannot instantiate a protocol type.

I would suggest using inheritance for this.

sawarren
  • 286
  • 1
  • 5
4

Public default init in protocol is a similar question.

Why does a class require having an init despite the protocol providing a default implementation? The answer is that it isn‘t providing a default implementation, but rather just another init, which requires the class‘ init() here to exist.

Since your protocol can't be certain to cover all members of the class that uses it, any initializer you declare in your protocol will need to delegate initialization of the "unknown" members of the class to another initializer provided by the class itself. Source

Your protocol init is however probably shadowed by the class‘ init() so you won‘t be able to use it. Maybe add a parameter to the class initializer/the protocol‘s required initializer or a parameter to the protocol-extension ìnit(). If the protocol-extension init isn‘t shadowed by the class you will be able to use it to init the class.

The init is required because the class can be subclassed and initializer aren‘t necessarily inherited, see inheritance doc and initialization doc under Initializer Inheritance and Overriding on this. If the init weren‘t required a subclass wouldn‘t be adhering to the protocol anymore, which is undesirable and so the init set in a protocol is only fullfiled by a required initializer.

Note: In required init() you tried to call another initializer but I think this is not possible except for initializing a parent since required init() is a designated initializer which cannot be chained in the same class, only convenience initializers can be chained in the same class.

You can see the protocol-extension ìnit as a convenience initializer because the init requires calling another initializer, like every other convenience initializer does with self.init(...).

Fabian
  • 5,040
  • 2
  • 23
  • 35
0

This answer is not pointing out the core issue of the OP. Kept here just for a record.


You are doing completely wrong. Protocol is not superclass.

Default implementation of protocol is just a default used when you do not implement a protocol required method. You cannot call default implementations of a protocol directly by any means.

Protocol is not a simple replacement of superclass. Change your way of thinking based on the protocol.

even though I know it's possible for instance methods.

You are mistaking something. You cannot call protocol's default implementation even if it's a method.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • Thanks for the reply. However, when you say _You cannot call protocol's default implementation even if it's a method_: this is not true, it is actually possible. See here for an example: https://stackoverflow.com/a/34777469/145997 – Romain Aug 13 '18 at 22:10
  • I'd be wary of telling people that they are (or are doing things) "completely wrong". It's not very nice; and even if it was, it doesn't leave a lot of room for you to make mistakes without losing face. – zneak Aug 13 '18 at 22:15
  • @Romain, it shows you are mistaking what the linked article means. When method declaration is removed, the method in the extension is not a default implementation. – OOPer Aug 13 '18 at 22:17
  • @zneak, thanks for your suggestion. I do not understand what you mean after _It's not very nice_, but I have found that my answer is just insisting on _You cannot call default implementations of a protocol directly by any means_, and omitting many other aspects or interests of the OP. – OOPer Aug 13 '18 at 22:33
  • @OOPer OK I get it. What I wanted to say with this linked article, is that what applies to methods in protocols and extensions, doesn't to initializers, and I wanted to understand why and if there was any viable solution. @sawarren and @Purpose both point out that initializers in protocols are `required`, thus forcing an explicit implementation which inevitably masks the "default implementation" in the protocol extension. And you're right, subclassing is certainly a better fit here. – Romain Aug 13 '18 at 22:45
  • @Romain, thanks for your patience. As I wrote in the comment to zneak, my answer was out of focus of your issue. And your _effort_ to finding something right in my answer tells me what _not very nice_ means in zneak's comment. – OOPer Aug 13 '18 at 22:52
  • 1
    No offense OOPer. I must admit that when I first read @zneak‘s comment, I felt a little bit relieved that I wasn’t finally the only one finding your post a little bit rough (even if correct). But that’s ok since you were right and allowed me to learn that I was indeed mistaking something. Also, thank you @zneak for your intervention! – Romain Aug 14 '18 at 10:20