iOS Today Extension Simple Tutorial

I would like to tell a little bit about how to create a simple Today Widget for iOS 8 and higher. We can find a lot of application in the store with widgets, really different widgets, such as weather, currencies, quick buttons for tracking something, location, etc. Example from my application, which displays currencies:

It’s not separated application, which you can submit to the store, it’s extension of your application, and you can’t submit only the widget.

Let’s start. First of all, add a new target to your project:

Choose options:

And create scheme:

Now you have MainInterface.storyboard and TodayViewController.swift. The main controller implements NCWidgetProviding protocol. In this protocol we have two methods:

// If implemented, the system will call at opportune times for the widget to update its state, both when the Notification Center is visible as well as in the background.
// An implementation is required to enable background updates.
// It’s expected that the widget will perform the work to update asynchronously and off the main thread as much as possible.
// Widgets should call the argument block when the work is complete, passing the appropriate ‘NCUpdateResult’.
// Widgets should NOT block returning from ‘viewWillAppear:’ on the results of this operation.
// Instead, widgets should load cached state in ‘viewWillAppear:’ in order to match the state of the view from the last ‘viewWillDisappear:’, then transition smoothly to the new data when it arrives.
@available(iOS 8.0, *)
optional public func widgetPerformUpdateWithCompletionHandler(completionHandler: (NCUpdateResult) -> Void)
// Widgets wishing to customize the default margin insets can return their preferred values.
// Widgets that choose not to implement this method will receive the default margin insets.
optional public func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets

In the first method you need to fetch a new data if you have, and you need to call completion handler with three possible values: NewData, NoData, Failed. And widget will be know, will update UI or use old snapshot.

In the second method Apple gives a chance to change margins. By default Today Widget has left margin, you can see in default Apple applications, such as Calendar, Stocks.

For example:

func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> (UIEdgeInsets) {
return UIEdgeInsetsZero

Let’s create simple extension. Please, add the TableView to interface and set up outlet.

For static height of the view you need to set up preferredContentSize:

self.preferredContentSize.height = 200

Implementation of loading data, for example from plist:

func loadData() {   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
if let path = NSBundle.mainBundle().pathForResource(“Data”, ofType: “plist”) {
if let array = NSArray(contentsOfFile: path) {
for item in array { as! NSDictionary)
  dispatch_async(dispatch_get_main_queue()) {

And of course simple implementation of TableView:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableViewCellIdentifier", forIndexPath: indexPath)
  let item = data[indexPath.row]
cell.textLabel?.text = item["title"] as? String
cell.textLabel?.textColor = UIColor.whiteColor()
  return cell

And finally we get the result:

Full code you can found here. Happy coding!

Like what you read? Give Maxim Bilan a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.