Photo from Unsplash.com

Build a World Clock in Objective-C

And learn about NSDate, NSCalendar & NSDateComponents.

Jen Sipila
11 min readMar 12, 2016

--

Last Updated: May 6, 2017.

How the finished demo looks.

In this tutorial we will be using Apple’s built-in time and date classes to make a functioning world clock which shows current date and time for 6 cities around the world.

I will also give information about how to use the classes useful for dealing with time and date in Objective-C.

This was made using Xcode 8.0 purely in Objective-C.

Complete project files can be found here.

Or Gist file of complete ViewController.m code here.

TL;DR World Clock Overview

In reality, a clock in iOS is just a label updated at regular intervals to show the most current time. This is done with the help of NSDate and it’s ability to get the most current via the date property and an NSTimer method called scheduledTimerWithTimeInterval: target: selector: userInfo: repeats: which continuously updates the time and date labels.

You’ll see in the code that this project has four main functions.

  1. The IBAction called cityButtonTapped: which will determine the city a user taps and call the method in charge of kicking off the timer, setTappedCityTimer.
  2. setTappedCityTimer initializes a timer which specifies options including the interval at which the timer should call the selector method, setDateTimeLabelsWithTimeZone.
  3. setDateTimeLabelsWithTimeZone sets the labels for the date and time with the help of the formatter method, formatCurrentDateTimeForTimeZone.
  4. Lastly, formatCurrentDateTimeForTimeZone which gets the current date and time at that moment in time and returns a nicely formatted NSString to be displayed.

Let’s get started!

Set up a new project in Xcode.

I’m going to assume you know the basics of using Xcode. (If you need help getting started refer to the following link: http://codewithchris.com/xcode-tutorial/ )

In the project you will need ViewController.h & ViewController.m files, and a Storyboard file.

Build the UI

(If you are only interested in the information on NSDate, NSCalendar, & NSDateFormatter and clock functionality skip to the sections below.)

The UI will consist of a large UILabel depicting time, a UILabel for date, & six UIButtons for the six cities.

  1. Set up labels and buttons in storyboard.
  2. Make outlets for the each of the UILabels and name them according to their city or purpose, i.e. timeLabel and dateLabel.
  3. Make one IBAction Method called ‘cityButtonTapped’ and connect all six buttons to it. Make sure they are being connected as IBActions.

Your starting ViewController.m file should look like this:

Clock Functionality

When the user taps any of the six buttons the cityButtonTapped: method will run.

But, how will our program know which city is selected?

Luckily, UIButton includes a tag property. Which will be accessible to us through sender.tag.

The tag is an integer assigned to each button through the storyboard. Since all of our UIButtons are connected to the same IBAction calling on the tag property will identify which one was tapped.

Note: Be careful! You need to be sure you arrange for each tag integer to correspond to the proper variable in the code. There are probably more ideal and safer ways of identifying which button has been tapped, but in the interest of focusing on building the clock I’ll use tags.

4. Give each button a tag. Go to storyboard and select a button. In the Attributes Inspector on the right hand side and scroll down to the view section. Here, add in a tag number.

Setting a button’s tag in storyboard.

Set a tag for each button. I used integers 100–105 to correspond to each buttons.

5. Next, let’s complete the cityButtonTapped: method.

This method should do two things:

First: Identify which button was tapped.

Second: Call a method containing the timer, which will in turn start the timer.

6. To handle the different button taps let’s make a switch statement!

You can see each of the cases correspond to the number we assigned to the buttons in storyboard(100–105) and we will access the tapped button’s tag by using sender.tag.

switch (sender.tag){   case 100:
break;
case 101:
break;
case 102:
break;
case 103:
break;
case 104:
break;

case 105:
break;
default:
break;
}

Inside of each case we will assign the corresponding timezone to a property.

7. Before specifying in the timezones. Let’s make the property that will hold the selected time zone.

@property(nonatomic, strong)NSString *timeZone;

8. Let’s also make another property to keep track of which button was tapped. This will come in handy for UI purposes at the end.

@property(nonatomic, strong)UIButton *selectedButton;

9. Now assign the corresponding timezones and buttons.

The complete cityButtonTapped: method will look like this.

You can display the time for any city as long as this time zone code reflects that location.

Here is a complete list of time zone codes.

10. Notice after the switch we call a new function. This function will kick off a new timer.

[self setTappedCityTimer];

Note: I used the word timer instead of clock. I did this because the ticking of the seconds is implemented with an NSTimer object, which you will see shortly.

11. Next let’s create the setTappedCityTimer: method and the timer property.

First, create a timer property:

@property(nonatomic, strong)NSTimer * timer;

Then make the setTappedCityTimer: method. Completed it should look like this:

Here is a description of what’s happening line-by-line.

Line 2: This invalidates the timer. Remember this code will be run each time the user taps on a new city. We want a completely new timer each time, so we invalidate any existing timer that may already be running.

Line 3: We set the timer to nil because we want to get rid of any existing timer object and make a whole new timer instance.

This cleans up the memory and is a necessary step to make sure the clock stays accurate.

Line 5: Here we call a new method getCurrentDateTimeForTimeZone: . This method is responsible for returning new time and date strings each time the timer ticks. This is the method called from the timer in Line 6 at each interval. I call it here once because it removes a lag in the clock when first opened.

Line 6: Here a new timer is created with scheduledTimerWithTimeInterval: target: selector: userInfo: repeats: .

NSTimer

The scheduledTimerWithTimeInterval: target: selector: userInfo: repeats: method from NSTimer makes the clock update by updating the timeLabel at a determined interval. This repetitive updating gives the illusion that the seconds are ticking by.

The interval should be 1.0 to display a naturalistic passing of seconds.

The target is self.

The selector is the method that will be fired off at the interval. Syntax should look like this: @selector(setDateTimeLabelsWithTimeZone).

UserInfo can be nil.

Repeats should be YES.

Date Classes

12. Next, let’s build the formatCurrentDateTimeForTimeZone method and talk about some helpful date classes.

When complete, it will look like this:

Line-by-Line

Lines 2 & 3: Here date and time formatter instances are declared, see below section on NSDateFormatter.

Line 4: A locale is declared as POSIX, see below section on NSLocale.

Lines 5: A time zone is declared and set to the existing value of the timeZone property, see below the section on NSTimeZone.

Lines 7 & 10: The date formatter is set to POSIX locale.

Lines 8 & 11: The date formatter takes the provided strings and waits to format the date and time correspondingly.

Lines 9 & 12: The date formatter takes the specified time zone for the selected city.

Line 14: The current time and date at the exact moment the function is run is recorded.

Lines 15 & 16: The date and time are converted to strings using the formatter strings from Lines 8 & 11.

Line 17: The formatted time and date strings are put in an array to be returned to the setDateTimeLabelsWithTimeZone, where they will be shown.

Lastly, we should probably kick off the clock when the user first opens the app, before they have selected a button. We can set NYC to the default running time.

13.We can do this simply by adding the following method to viewDidLoad.

[self.nyc sendActionsForControlEvents: UIControlEventTouchUpInside];

This artificially taps the nyc UIButton when viewDidLoad runs.

Now, run the project your world clock should be functioning!

//////Coming Soon: video of world clock at this point.

Let’s complete the final step: adding a UI indicator to show which city is currently selected.

Highlighting Selected City Button

Last, you need to show the user which city is selected and corresponds to the time displayed. This might seem like a UI embellishment, but it is essential to the functionality of the clock.

Note: There are different ways of achieving this. I’ve decided to change the color of the selected button to indicate selection. This could be done more easily, perhaps, by putting the buttons in a table view and using didSelectRowAtIndexPath to change only the selected buttons text color.

14. Let’s write two new functions: one that will change the text color of the selected button to yellow, and another that will change all of the unselected button’s text color to blue.

This can be done within the body of one function, but conceptually I like to separate them.

- (void)highlightSelected { 
self.selectedButton.tintColor = [UIColor yellowColor];
}

To highlight the selected we will just set the tintColor of the selectedButton property to a yellow color.

To “unhighlight” we will make an array of all of the city buttons. Then a mutable array that will be populated with the unselected cities. We’ll loop through the cities and if they are not equal to the selected city they get put in the unselected array. At the end, calling the setValue method on the unselected array and setting the tintColor for all to blue.

15.Lastly, call these methods in two places.

Within the setTappedCityTimer method call both methods after setting the timer object to nil.

[self highlightSelected];[self unhighlightDeselected];

Then, in viewDidLoad call just

[self highlightSelected];

The default color of the buttons is already blue when they are first initialized so only the highlighting is necessary.

Now your world clock is finished!!

Check out the complete ViewController.m file here. Or the completed project with all files here.

//////Coming Soon: video of fully functioning world clock

Scroll to the bottom for helpful links or continue reading to learn about about each of the classes we used and more!

NSDate

The NSDate class provides a function that easily returns a time stamp for the current date and time.

NSDate *now = [NSDate date];

The time stamp looks something like this:

2017–05–07 12:20:13 +0000

NSDate is makes it easy to get the current time using only one line of code, but almost always want that date to be displayed in a specific way.

To handle this problem we have NSDateFormatter.

NSDateFormatter

NSDateFormatter converts the NSDate value into string format, and allows complete control of what that date looks like.

The date formatter is a string which tells how the measure of time should be displayed.

There are two ways to format NSDate values into NSStrings, localized format strings provided by Apple and custom format strings.

Localized Format Strings

The following are localized format strings:

NSDateFormatterShortStyle, NSDateFormatterMediumStyle, NSDateFormatterLongStyle, NSDateFormatterFullStyle, & NSDateFormatterNoStyle.

Note:NSDateFormatterNoStyle is used to omit a time or date from a particular string.

These will perform pre-defined styles of formatting.

Using a localized date formatter looks like this:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];

For the tutorial, we will use custom NSDateFormatters.

Custom NSDateFormatters

In this demo we will format the time and date separately to appear on their own respective labels.

To make a custom NSDateFormatter, we should first declare an instance of the formatter.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

To use customization occurs through the use of the setDateFormat: method and the formatter string. The formatter string argument takes a string and will directly set how the date/time appears.

[dateFormatter setDateFormat:@dd.MM.yy];  //Resulting date will look like:07.05.17

Make sure to visit http://nsdateformatter.com/ to see a complete list of formatter strings.

You can see on Line 8, we represent the date with the full month, full day name, and year the formatter string will look like this:

[dateFormatter setDateFormat:@"EEEE MMMM dd y"];//Our date will look like: Sunday May 7 2017

The following format the current date and time by first declaring the formatter, then getting the date and lastly applying the formatter.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];[formatter setDateFormat:@"EEEE MMMM dd y"];// Date to string
NSDate *now = [NSDate date];
NSString *formattedDate = [formatter stringFromDate:now];

Time Formatting

Formatting time also uses formatters.

[timeFormatter setDateFormat:@"h:mm:ss a"];//Our resulting formatted time will look like: 12:20:13 PM

Two additional steps are needed to ensure the accuracy of the date and time: NSLocale and NSTimeZone.

NSLocale & NSTimeZone

NSLocale

On Lines 4, 7, & 10, we declare as set the Locale.

NSLocale is lets the NSDateFormatter know what kind of location based standards to follow, like language & numerical system.

The locale used for apps running in the United States is called POSIX and is represented by the string “en_US_POSIX”. Other possible locales are listed here: https://gist.github.com/TonnyXu/3424842.

But for now we will need to include the locale for the United States, by initializing a local with POSIX and setting the locale to the date.

NSLocale *posix = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[date setLocale:posix];

Lastly, in this method we specify the time zone of the currently selected city.

NSTimeZone

Time zones are represented either by three letter abbreviations, like “GMT” (for Greenwich Mean Time), or by the complete time zone name “America/New_York”.

A complete list of time zone codes can be found by printing result to the following method call [NSTimeZone abbreviationDictionary]; .

You can declare an instance of the time zone in one of two ways:

NSTimeZone *greenwichMeanTime = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];NSTimeZone *newYorkTime = [NSTimeZone timeZoneWithName:@"America/New_York"];

In our case we use in Line 5:

NSTimeZone *localTimeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZone];

Then we set the time zone to the date formatter in Lines 8 & 11:

[dateFormatter setTimeZone:localTimeZone];[timeFormatter setTimeZone:localTimeZone];

There are other important Apple classes that were not needed in this tutorial which handle time and date.

NSCalendar & NSDateComponents

NSCalendar is a very useful for interpreting NSDates and, even though it was not necessary for this tutorial, it is worth mentioning what it can do.

Using NSCalendar’s initWithCalendarIdentifier: method defines which calendar to use to define the length of days, weeks, months, years etc.

NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];

These are the possible calendars(though the most common is the Gregorian Calendar):

NSGregorianCalendar, NSBuddhistCalendar, NSChineseCalendar, NSPersianCalendar, NSHebrewCalendar, NSIslamicCalendar, NSIslamicCivilCalendar, NSJapaneseCalendar, NSRepublicOfChinaCalendar, NSIndianCalendar, NSISO8601Calendar.

Once the calendar is defined, the measures of time can be formed into components. NSCalendarUnits divide the NSDate value into measured units of time, called NSDateComponents. The calendar units can be used as needed to portray the date and time.

NSDate *now = [NSDate date];
NSCalendar *calendar = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSCalendarUnit units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *components = [calendar components:units fromDate:now];

These components can also be reverted back to an NSDate or used to perform calculations with time.

--

--