Search code examples
swiftpropertiesoverridingreadonlyread-write

How to override readonly property in Swift subclass, make it read-write, and assign to superclass?


How can I override readonly property of a Swift superclass, from a subclass, to make the property read-write?

I want to do that, because that is what the iOS UIResponder documentation says is required. However, I'm getting an error when I try to implement what I think the Swift 5 documentation says can be done:

Swift 5 documentation

Inheritance

Overriding Property Getters and Setters:
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override.

What Went Wrong:

Based on aforementioned Swift docs statement, for which I found no accompanying example, I created the following subclass, at which XCode generates this error message:

   "Cannot assign to property: 'inputAccessoryViewController' is a get-only property"

My Subclass:

class InputAccessoryEnabledTextView : UITextView {
    override var inputAccessoryViewController: UIInputViewController? {
        get { super.inputAccessoryViewController }
        set { super.inputAccessoryViewController = newValue }
    }
}

UIResponder

Declaration

var inputAccessoryViewController: UIInputViewController? { get }

Discussion

This property is typically used to attach an accessory view controller to the system-supplied keyboard that is presented for UITextField and UITextView objects. The value of this read-only property is nil. If you want to attach custom controls to a system-supplied input view controller (such as the system keyboard) or to a custom input view (one you provide in the inputViewController property), redeclare this property as read-write in a UIResponder subclass. You can then use this property to manage a custom accessory view. When the receiver becomes the first responder, the responder infrastructure attaches the accessory view to the appropriate input view before displaying it.

Note: I do see a question from 6 years ago on Stack Overflow that got no accepted answers and doesn't seem to properly answer the question, so please don't flag this as a dup without a good reason. Perhaps these questions can be merged later


Solution

  • super.inputAccessoryViewController is not settable.

    Your overridden implementation in the subclass, self.inputAccessoryViewController is.

    By adding a setter to the property in a subclass, you don't automatically also add the same thing in the superclass. What's in the subclass stays in the subclass.

    So it's not that you can't override a property by adding a setter, you just can't set this here:

    override var inputAccessoryViewController: UIInputViewController? {
        get { super.inputAccessoryViewController }
        set { super.inputAccessoryViewController = newValue }
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    }
    

    You can do other things, like:

    override var inputAccessoryViewController: UIInputViewController? {
        get { super.inputAccessoryViewController }
        set { print("I just go set to \(newValue)") }
    }
    

    But that's not very useful. What you want is probably:

    private var myInputAccessoryController: UIInputViewController?
    
    override var inputAccessoryViewController: UIInputViewController? {
        get { myInputAccessoryController }
        set { myInputAccessoryController = newValue }
    }