I am currently developing a very simple Live Scores MAC OSX app for personal use where I show a bunch of labels (scores) on the touch bar. What I am trying to achieve in a few steps:
- Fetch live soccer scores from a 3rd party API every 30 seconds
- Parse the scores and make them into labels
- Update the touch bar with new scores
[Please note here that this app will not be published anywhere, and is only for personal use. I am aware of the fact that Apple strictly advises against such type of content in the Touch Bar.]
Here is the code that I wrote following basic Touch Bar tutorial from RW (https://www.raywenderlich.com/883-how-to-use-nstouchbar-on-macos). Skeleton of my code is picked from the RW tutorial:
- In
WindowController
(StoryBoard entry point), overridemakeTouchBar
like this:
override func makeTouchBar() -> NSTouchBar? {
guard let viewController = contentViewController as? ViewController else {
return nil
}
return viewController.makeTouchBar()
}
- In
ViewController
, which is also the Touch Bar Delegate, implement themakeTouchBar
fn:
override func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = .scoresBar
touchBar.defaultItemIdentifiers = [.match1, .flexibleSpace, .match2, ... , .match10]
return touchBar
}
NSTouchBarDelegate
inViewController
.scores
is where I store my fetched scores (See 5). I returnnil
for views if scores aren't fetched yet:
extension ViewController: NSTouchBarDelegate {
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
if (<scores not fetched yet>) {
return nil
}
// matchNum is the match number for which I am showing scores for
let customViewItem = NSCustomTouchBarItem(identifier: identifier)
customViewItem.view = NSTextField(labelWithString: self.scores[matchNum ?? 0])
return customViewItem
}
}
- To fetch scores periodically I am running a scheduled task Timer in
viewDidLoad()
of my viewcontroller like this:
_ = Timer.scheduledTimer(timeInterval: 30.0, target: self, selector: #selector(ViewController.fetchScores), userInfo: nil, repeats: true)
- And finally, this is my
fetchScores
function that also makes a call to update the Touch Bar:
@objc func fetchScores() {
let url = "<scores api end point>"
Alamofire.request(url).responseJSON { response in
if let json = response.result.value {
// update self.scores here and fill it with latest scores
if #available(OSX 10.12.2, *) {
//self.touchBar = nil
self.touchBar = self.makeTouchBar() // This is where I am calling makeTouchBar again to update Touch Bar content dynamically
}
}
}
My understanding from the code above is that once I make a call to makeTouchBar
in fetchScores
and assign it to my viewcontroller's touchBar
property, it should ideally call touchBar(:makeItemForIdentifier)
delegate function to update the Touch Bar view (SO thread on this). But in my case, touchBar(:makeItemForIdentifier)
is never called. The only time touchBar(:makeItemForIdentifier)
is called is the first time, when makeTouchBar
is called from my WindowController (See point 1 above). And since scores have not been retrieved yet, my touch bar remains empty.