2

I have a Child class that contains methods that I define and methods inherited from its Parent class. I want some of the methods from Parent to be unavailable to the user or at least generate a compile error/warning when used. Example:

class Parent {
    public func secretMethod() { // Don't want anyone to use
    }
}

class Child: Parent {
    public func overtMethod() { // Fine with people using
    }
}

Child.init().overtMethod()  // Fine
Child.init().secretMethod() // Not Fine. Should not be viewable/useable/accessible

I cannot change the parent class. How I long to be able to set secretMethod to private. Swift doesn't allow restricting the access level when overriding functions, so I can't set secretMethod to private in the Child class either.

So far I have been using @available to mark the method with a compile warning and message, but I am hoping that there is a better way? I considered overriding and throwing an error, but that can't be done if the original method isn't marked throws.

Thank you.

Edit: I also cannot override the secretMethod to make it unavailable. The Child class needs to access it.

GranolaGuy
  • 133
  • 12
  • I'm confused as for when you want to allow access to the "secret" method. You don't want to set it to `private`, but also do? Please explain. – George Dec 18 '18 at 17:17
  • 1
    What you are trying to achieve is possible violation of Liskov's Substitution Principle which may result in bad design for your project. Visit for more: https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle – prex Dec 18 '18 at 17:38
  • @George_E, I do not have control over the Parent class – GranolaGuy Dec 18 '18 at 17:51
  • Your suggestion of using @available was a great idea and thanks! – David H Jul 31 '20 at 19:29

4 Answers4

3

Rethink your design.

I think you may be only considering the "user" (another developer? .. you are writing a library, I guess?) and not existing source. The basic idea of subclassing is that the subclass will be able to be used anywhere the parent is used.

class A: UIView {
  func availableMethod() {
  }
  // func layoutSubviews() ----- let's say you want to hide this method
}

The problem arises here from you not knowing what any other internal classes may be doing with the layoutSubviews and if you somehow block it on an instance of A, then anything expecting to be able to use that instance as though it is an instance of UIView will now fail.

Ian MacDonald
  • 13,472
  • 2
  • 30
  • 51
  • Nail on the head. I am writing a library and that is why the `secretMethod` needs to continue to exist. I just don't want other developers using it when they use my library. I have provided equivalent methods through the `Child` class to use if they want to do what `secretMethod` does. – GranolaGuy Dec 18 '18 at 17:32
  • I'm going to attempt a different approach and see if it works. While the current model functions, I don't like this swamp I find myself in. – GranolaGuy Dec 18 '18 at 17:52
2

Don't use inheritance if you don't want to expose public methods of the parent class. You can hide the 'parent' class as private instance in your 'child' class and only forward the methods you want to expose.

If you are relying on inheritance of your child class to your parent, because of some APIs which expect the parent class, then you also cannot make any parent methods private. This would break the contract between the API using the parent class, if you pass a child class, which hides formerly public parent methods. That won't work.

nikwest
  • 105
  • 1
  • 6
2

Look, what you're looking for is protected access modifier which would allow you to have methods and variables visible within its descendants.

But, in Swift there isn't any. The main access modifiers are private or internal.

Somewhere between them is fileprivate. It allows you to have visible methods and variables within one file. Even if this can be solution for you, it may not be right design pattern.


If you're writing library and you don't want to allow other programmers to call methods from parent but you want to allow them to see subclasses' methods, you can override internal function from parent to public or open function in subclass

Robert Dresler
  • 10,580
  • 2
  • 22
  • 40
2

I would like to enhance on the answer of @nikwest. You should try to use composition instead of inheritance. So Parent and Child don't need to share any code at all but some methods are available on both. You could also create a common ancestor so a certain var will always exist.

class Parent: Overt {
}

class Child: Overt, Secret {
}

protocol Overt { 
    func overtMethod() 
}
protocol Secret {
    func secretMethod() 
}

extension Overt {
    public func overtMethod() { // stuff }
}

extension Secret {
    public func secretMethod() { // stuff }
}

A variation on this when you have a common var to share or something (like UIViewControllers using the composition pattern) could use a CommonParent that has the var and then Child1 and Child2 would add the protocols they need to come to the required functionality. If you don't use the default implementation in the extension you can still override.

Lucas van Dongen
  • 9,328
  • 7
  • 39
  • 60
  • This is indeed what I would like to do, but I cannot modify the Parent. – GranolaGuy Dec 18 '18 at 19:30
  • Perhaps I would understand the problem better if it was less abstract. The `Parent` class is an iOS SDK class func that is public already? Is the original function Objective-C? If so, after deprecating the override could you simply swizzle it for the correct implementation? – Lucas van Dongen Dec 18 '18 at 20:23
  • I mean: you deprecate, you print a warning and your reroute it to the correct method. What else can a developer do to make it ---idiot--- _user_ proof? – Lucas van Dongen Dec 18 '18 at 20:25
  • Yes, `Parent` class is an iOS SDK class func that is public already. It is Obj-c. I am currently deprecating and overriding the `secretMethod` from the parent. Swizzle? – GranolaGuy Jan 02 '19 at 17:10
  • Swizzling: https://nshipster.com/method-swizzling/ it means that you can replace the implementation of a method with something else. You can do this for any class even Apple SDK ones. Very powerful. This only works in Objective-C. – Lucas van Dongen Jan 03 '19 at 18:19