Need help with GrandCentralBoard?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

206 Stars 16 Forks GNU General Public License v3.0 862 Commits 18 Opened issues


Hang a TV in your open space or team room to show everyone what's up and get them up to speed.

Services available


Need anything else?

Contributors list

Grand Central Board for the Apple TV


Build Status Swift2.2 Version License Platform Documentation

Hang a TV in your open space or team room to show everyone what's up and get them up to speed.

The board is a lightweight piece of code. The TV screen is to be used in landscape orientation and will be split into six rectangular widgets loaded from a remote configuration file. This is just a UIView, so you can use the space in any way you want. Updating the widgets is standardized, though, therefore you should not ignore this convention.

👷 Project maintained by: @nsmeme (Oktawian Chojnacki)

✋ Don't even ask - it's obviously written entirely in ♥️ Swift 2.2.


The small set of sources needed to load configuration, initialize and show the Board.


CocoaPods is a dependency manager for Cocoa projects.

You can install it with the following command:

$ gem install cocoapods

To integrate GCBCore into your Xcode project using CocoaPods, add this to your

pod 'GCBCore', '~> 1.0'

Then, run the following command:

$ pod install


This is how you configure Grand Central Board:

let autoStack = AutoStack()
let scheduler = Scheduler()
let dataDownloader = DataDownloader()

let availableBuilders: [WidgetBuilding] = [ ImageWidgetBuilder(dataDownloader: dataDownloader), ]

let configFileName = NSBundle.localConfigurationFileName let configurationFetching = LocalConfigurationLoader(configFileName: configFileName, availableBuilders: availableBuilders)

let boardController = GrandCentralBoardController(scheduler: self.scheduler, stack: self.autoStack)

let configurationRefresher = ConfigurationRefresher(interval: configRefreshInterval, configuree: boardController, fetcher: configurationFetching)

// in ViewController:

override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated)

view = autoStack




If you want to contribute, please add an issue and discuss your plans with us. This will allow us to give you assistance should you need it and to make sure that people aren’t working on the same things.

If you want to open a Pull Request, make sure you have SwiftLint installed, and check that your project does not generate warnings or errors. Any warning will cause Travis build script to fail.


Adding a new Widget

There is a separate article ( covering adding new widgets.


A Widget consists of four main components:

  • View: a view implementing
    protocol that displays the information.
  • Source: implements one of the updating strategies (further described below).
  • Widget: a controller class implementing
    protocol that is exposed to the scheduler and connecting previous two components with each other.
  • WidgetBuilder: implements
    protocol, instantiate Widget with settings from configuration file.


Widget canvas for 1080p:

  • 640pt x 540pt

This size is constant and won't change on tvOS.

The GCBCore v2.0 will support iOS target but the canvas sizes are yet to be defined.


There are two ways to configure Grand Central Board: with a remote or bundled configuration file. A

file formatted like this is used to configure the Grand Central Board:
{ "widgets":[ 
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} },
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} },
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} },
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} },
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} },
    {"name":"somewatch", "settings":  {"timeZone":"Europe/Warsaw"} }

NOTE: Each widget will have its own settings properties.

The way a configuration file is loaded is specified in

file. image

Remote configuration file

To have the configuration file loaded from a remote location, edit

and set
to the desired location. Make sure
is set to

Bundled configuration file

To use a bundled configuration file, set

. If necessary edit
and check if the file is listed under
Copy Bundle Resources
Build Phases
. For development purposes you can also launch the application via

View States

Widget view should show these states:

  • Waiting - starting state, presenting some activity indicator.
  • Rendering - presenting information (after render method is called).
  • Failed - data failed to load, should be avoided if possible.


The source should implement one of two protocols:

  • Synchronous - the source will return value synchronously in a non-blocking way.
protocol Synchronous : Source {
    func read() -> ResultType
  • Asynchronous - the source will call the provided block after the value is retrieved (only once).
protocol Asynchronous : Source {
    func read(closure: (ResultType) -> Void)
  • Subscribable - the source will call the provided block each time a new value arrives (multiple times). Note that
    can and often will be ignored.
public protocol Subscribable : Source {
    var subscriptionBlock: ((ResultType) -> Void)? { get set }

Fail can be handled silently, but there may be Widgets for which the fail state should be presented, the choice is up to you.

All strategies inherit the Source protocol:

public enum SourceType {
    case Cumulative
    case Momentary

public protocol UpdatingSource : class { var interval: NSTimeInterval { get } }

public protocol Source : UpdatingSource {

associatedtype ResultType

var sourceType: SourceType { get }



We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.