Created
July 27, 2017 23:23
-
-
Save amelnychuck/4cc03b252d2f8ebf8a8b05dade7d07e5 to your computer and use it in GitHub Desktop.
Example of why we need closures to replace the last instances of #selectors in Cocoa Touch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
class TabBarViewController: UITabBarController { | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
} | |
// MARK: Keyboard Commands | |
override internal var keyCommands: [UIKeyCommand]? { | |
guard let controllers = viewControllers else { | |
return nil | |
} | |
var commands = [UIKeyCommand]() | |
for controller in controllers { | |
if let index = tabBar.items?.index(of: controller.tabBarItem) { | |
var pressedAction: Selector? | |
switch index { | |
case 0: | |
pressedAction = #selector(TabBarViewController.pressed1) | |
case 1: | |
pressedAction = #selector(TabBarViewController.pressed2) | |
case 2: | |
pressedAction = #selector(TabBarViewController.pressed3) | |
default: | |
break | |
} | |
if let action = pressedAction { | |
let command = UIKeyCommand(input: String(describing: index + 1), modifierFlags: .command, action: action, discoverabilityTitle: controller.tabBarItem.title!) | |
commands.append(command) | |
} | |
} | |
} | |
return commands | |
} | |
@objc func pressed1() { | |
selectedIndex = 0 | |
} | |
@objc func pressed2() { | |
selectedIndex = 1 | |
} | |
@objc func pressed3() { | |
selectedIndex = 2 | |
} | |
} |
Explored some additional choises; (I find syntax to be quite intriguing... pondering about writing my own...:) )
Swift; more functional
import UIKit
class TabBarViewController: UITabBarController {
// MARK: Keyboard Commands
var keyCommandsMap:[String:Int] = [:]
override internal var keyCommands: [UIKeyCommand]? {
let getInput = { (i: Int) -> String in
let key = "\(i + 1)"
return keyCommandsMap[key] = i
}
return viewControllers?.map {$0.tabBarItem}.map {(
index: tabBar.items?.index(of: $0),
item: $0
)}.filter {$0.index != nil}.map {UIKeyCommand(
input: getInput($0.index!),
modifierFlags: .command,
action: #selector(TabBarViewController.pressed),
discoverabilityTitle: $0.item.title!
)}
}
@objc func pressed(sender: UIKeyCommand) {
guard let index = keyCommandsMap[sender.input] else { return }
selectedIndex = index
}
}
Swift; if closures were supported instead of selectors
import UIKit
class TabBarViewController: UITabBarController {
// MARK: Keyboard Commands
override internal var keyCommands: [UIKeyCommand]? {
return viewControllers?.map {$0.tabBarItem}.map {(
index: tabBar.items?.index(of: $0),
item: $0
)}.filter {$0.index != nil}.map {UIKeyCommand(
input: "\($0.index! + 1)",
modifierFlags: .command,
discoverabilityTitle: $0.item.title!
) { [weak self] in self.selectedIndex = $0.index! }}
}
}
LiteScript; Superset of ES7 JavaScript
import { UITabBarController, UIKeyCommand } from 'UIKit'
class TabBarViewController extends UITabBarController
keyCommands() -get>
this.viewControllers?.map(v-> v.tabBarItem)
.map(item-> ({item, index: tabBar.items?.indexOf(item)}))
.filter(({index})-> index)
.map(({index, item})-> new UIKeyCommand({
input: index+1+''
modifierFlags: 'command'
discoverabilityTitle: item.title
action: ()=> this.selectedIndex = index
})
Custom syntax; direct translation
import from UIKit: UITabBarController, UIKeyCommand
TabBarViewController inherits UITabBarController
keyCommands: viewControllers?
| map: .tabBarItem
| map as item: {item, index: tabBar.items?.indexOf item}
| filter: .index?
| map: UIKeyCommand
input: .index+1
modifiers: :command
title: .item.title
on action: selectedIndex= .index
Custom syntax; how I think I would like to write it
import from UIKit: UITabBarController, UIKeyCommand
TabBarViewController inherits UITabBarController
keyCommands: if viewControllers: iterate them:
item: its tabBarItem
index: tabBar.items?.indexOf item
skip if !index
yield UIKeyCommand
input: index+1
modifiers: :command
title: item.title
on action: selectedIndex= index
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'd almost always advocate functional programming style and closures all the way, was expecting Apple to have block support for everything callback related when it was introduced in Objective-C way back.... even though that would require quite some code additions from their side...
However, with swift and bridging, they could've, possibly, added auto support / generation for closures-to-selectors, requiring no change on the massive amounts of API's from their side... (well, it is open-source, not sure they like auto-gen though...).
Nevertheless, the functionality you're asking for is still absolutely possible.
Ps. guards is a great feature! This code is almost 50% less indented ;)