Andrei Herford
Andrei Herford

Reputation: 18729

How to implement "abstract" property initializers in Swift

So far I have only worked on Objectiv-C projects and now started my first Swift project.

I know that Swift does not support abstract classes but I would like to know what is the best way to model / solve this in Swift:

// Abstract implementation
public abstract class MyClass {
    private SomeBaseClass someProperty; 

    MyClass() {
        initProperties();
    }

    abstract void initProperties();  // Init someProperty with some child class of SomeBaseClass.
}

// Objectiv-C
@implementation MyClass

- (id)init {
    self = [super init];

    if (self) {
        [self initProperties];     
    }

    return self;
}

- (void)initProperties {
    // Override in inherited classes
}


// Swift
class MyClass {
    // Should not be optional since MyClass should be required to have someProperty != nil
    let someProperty: SomeBaseClass;

    override init() {
        super.init();
        initProperties();
    }

    func initProperties() {
         // Cannot be empty since someProperty is non-optional and needs to be initialized
         // Cannot be empty since Swift does not support abstract methods
    }
}

Of course it would be possible to define someProperty as optional SomeBaseClass? but in this case every time the property is used it has to be tested and unwrapped.

Is there a better way to solve this?

EDIT:

I know that Swift uses protocols to create an abstraction similar to abstract classes. However I do not understand how this concept can solve the concrete problem / question.

In other programming languages the abstract class MyClass can use the property someProperty in many different places while leaving the burden to initialize the property with a value with its concrete subclasses.

Although I read the article linked by @MohamendS and the answers to the possible dublicate answer I do not understand how to achieve the same using protocols.

I guess I can't see the wood for the trees, but how can protocols help here?

Upvotes: 3

Views: 1362

Answers (2)

Mohmmad S
Mohmmad S

Reputation: 5088

Abstraction concept in Swift is used with protocols, i suggest reading this article to know more and here is an example

 protocol Abstraction {
    var foo: String { get }
    func fee()
    init(with foo: String)
}

class A: Abstraction {

    required init(with foo: String) {
        self.foo = foo 
    }
    var foo: String = ""

    func fee() {

      }
}

Edit: on your point, that

protocols can't implement functions

You can't but what you can do is extends those protocols using extension and give them an initial implementation therefore you don't have to implement them in the class and you can when you feel you'd like to, check the code below

    class A: Abstraction {

    required init(with foo: String) {
        self.foo = foo
    }
    var foo: String = ""

    //you don't have to implement func foo anymore as its extended in the extension
}

extension Abstraction {
    func fee() {
        print("ok")
    }
}

let a = A(with: "foo")
a.fee() // this will trigger the extension implementation,

Now to use init inside the extension body so you wont have to type them in each confirmation, check out the code below

protocol Abstraction {
    var foo: String { get set }
    func fee()
    init()
    init(with foo: String)
}


class A: Abstraction {
    required init() { }
    var foo: String = ""

    //you don't have to implement func foo anymore as its extended in the extension 
   // you don't have to implement the custom init anymore too 

}

extension Abstraction {
    init(with foo: String) {
        self.init()
        self.foo = foo
    }
    func fee() {
        print("ok")
    }
}

Upvotes: 4

Rob Napier
Rob Napier

Reputation: 299345

There are many possible answers to this question depending on how MyClass is used. As written, there's of course no reason for someProperty to be in the base class at all, and there's definitely no reason for an initProperties() method (that should be in init).

I realize this is "just an example," but it demonstrates a common problem of creating hierarchies that aren't needed. There are ways to write this code using a semi-abstract base class, but generally that should be avoided, so the first question is what you're using this for and can we avoid this problem entirely?

To answer the question as given, you'd probably start by making a default SomeBaseClass, so that the abstract class can just assign someProperty = SomeBaseClass().

If that's impossible, generally you'd use a ! type:

let someProperty: SomeBaseClass!

And you implement initProperties() with a fatalError:

func initProperties() { fatalError("Implement in subclass") }

Alternately, it can be convenient to implement someProperty as a computed variable, and implement it based on some other property in the subclasses

var someProperty: SomeBaseClass { fatalError() }

But this is really a last resort. Any time you find yourself having to write fatalError you're probably on the wrong track, and you don't need a trick to get around it; you need to reconsider the problem.

You should first think about how MyClass is used, and consider whether it can be a value type. Separately, you should think about whether it can be a protocol that matches the use case. Protocols are not just abstract interfaces that hide implementations. They are a view onto a conforming type to solve a specific problem. That's why there's a Collection protocol that provides access to dozens of algorithms for numerous, otherwise unrelated types, not an ArrayProtocol just to hide the implementation of Array. Don't turn MyClass into MyClassProtocol. Ask what kinds of algorithms want to use types like this one.

When you find yourself creating interlocking hierarchies of types (subclasses of something that require subclasses of some other thing), you have often sliced the problem in the wrong direction. You should rethink whether you could slice the problem so that the varying parts of SomeBaseClass are actually part of MyClass (often this makes SomeBaseClass simpler; for example being pure data rather than having logic).

There's no one right answer here. It depends on the nature of MyClass, so we can't really discuss it in abstract terms. Like abstract classes, solving abstract problems often leads you down the wrong roads. It's often better to start with concrete types and then find their similarities and extract them.

Even with that said, it's worth showing what a simple, naive protocol would look like here. (It's possible this is even the correct protocol.)

public protocol MyProtocol {
    var someProperty: SomeBaseClass { get }
}

That's it. That's all you actually express in your current abstract class (and it's not actually clear whether someProperty is public; if it's private, this protocol would be empty).

An implementing struct would then look like:

struct MyStruct: MyProtocol {
    var someProperty: SomeBaseClass
}

Or if you wanted a reference type, you could use a final class:

final class MyClass: MyProtocol {
    var someProperty: SomeBaseClass
    init() {
        someProperty = ...
    }
}

Or if you wanted inheritance, you could use a non-final class:

class MyClass: MyProtocol {
    var someProperty: SomeBaseClass
    init() {
        someProperty = ...
    }
}

Upvotes: 3

Related Questions