Search code examples
iosswiftpropertiesuikituikeycommand

add variable to all UIViewControllers


I'm new to Swift and I'm trying to implement a custom UIKeyCommand architecture in a practice app. I wrote the extension below for the base UISplitViewController to show all UIKeyCommands in the current views on screen.

extension UISplitViewController {
    open override var canBecomeFirstResponder: Bool {
        return true
    }

    var BPKeyCommands: [BPKeyCommand]? {
        var commands: [BPKeyCommand] = []

        var mastervc = self.viewControllers.first
        if (mastervc is UINavigationController) {
            mastervc = (mastervc as! UINavigationController).viewControllers.last
        }
        if let masterCommands = mastervc.commands {
            for command in masterCommands {
                commands.append(command)
            }
        }

        return commands
    }

    open override var keyCommands: [UIKeyCommand]? {
        var commands: [UIKeyCommand] = []

        if let bpkeycommands = BPKeyCommands {
            for command in bpkeycommands {
                let new = UIKeyCommand(input: command.input,
                                       modifierFlags: command.modifierFlags,
                                       action: #selector(executeKeyCommand(sender:)),
                                       discoverabilityTitle: command.title)
                commands.append(new)
            }
        }

        return commands
    }

    @objc private func executeKeyCommand(sender: UIKeyCommand) {
        if let index = keyCommands?.firstIndex(of: sender) {
            if let command = BPKeyCommands?[index] {
                command.action(command)
            }
        }
    }
}

Now, as you might expect this throws an error at if let masterCommands = mastervc.commands {, because UIViewController doesn't contain the commands variable out of the box. My question is: how can I haveUIViewControllerhave that variable? Just like all controllers can overridekeyCommands` by default?


Solution

  • You have to create a protocol with command variable and make your view controller conform to it (step 1). Either you can provide values for particular view controller or you can provide a default implementation.

    step 1:- Create a protocol with the variable you need.

    protocol Commandable{
       var commands: [String]{set get}
    }
    extension Commandable{
       var commands: [String]{
            get{return ["hello","world"]}
            set{}
        }
    }
    

    step 2:- Make then controllers which you are using conform it

    step 3:- change the above code to get commands

    if let commandProtocol = masterVC as? Commandable
    {
        let commands = commandProtocol.commands
    }
    else{
        // handle it
    }
    

    Make sure the variable is unique so you don't accidentally override it.

    Thank you.