iOS SDK: Working with Google Calendars

This tutorial will teach you how to build an app that will interact with the Google Calendar web service using OAuth 2.0. Read on!


Where We Left Off...

In my last tutorial, I showed you how to implement the OAuth 2.0 protocol in order to access Google services. By the end of that tutorial, we had built a fully functional class for accessing Google Services with OAuth 2.0. In this tutorial, I am going to put this class in action by implementing a demo app. Specifically, I am going to show you how to interact with the Google Calendar web service.


Project Overview

The app we'll build in this tutorial is going to let users get connected to their Google account, download their calendars, and create a new event with a description and a date/time. The new event will be posted to a calendar that the user selects.

Regarding our app structure, the basic view is going to be a table view that will contain three sections for setting the following data:

  • An event description
  • An event date/time
  • A target calendar

As far as the event description is concerned, a textfield will appear on the cell when the user edits the description and it will go away when he finishes doing so. For ease of use, an Input Accessory View will appear above the keyboard every time that the textfield is displayed.

For setting or changing the event date and time, another view (not a view controller) is going to be used. This view will contain a date picker view, from which the user will be able to pick a date. An event can be an all-day event, so no specific time needs to be set. For this case, a button will be used to set the date picker contents. For events occurring at a specific time, the date picker will display both date and time. For all-day events, only the date will be displayed. The view that contains the date picker (and a toolbar with the necessary bar button items as well) will be added as a subview to the view of our view controller when the user taps on the row of the second section of the table view.

Finally, the calendar section is going to be multi-functional. When the user is not yet signed into their Google account, only one row is going to exist in the section with a message that prompts the user to download their calendars. When the user selects this, the authorization process will be put in action. Once the access token has been obtained, an API call will be made to get the calendar list from Google. After the calendar list has been taken, the first calendar on the list is going to replace the prompting message on the row and become the selected calendar by default.

In addition to all of the above, a toolbar will exist under the table view and it will contain two bar button items. One button will be for posting the event on the selected calendar and a second button will be used to log out.

Let's get started!


1. Create a New Project

Step 1

Launch Xcode and create a new project. Select the Single View Application option and click Next:

gt6_1_project_template

In the Product Name field, add the GoogleCalendarPostDemo value. Of course, you may choose another name if you'd like. Also, make sure to check the Use Automatic Reference Counting option and uncheck everything else. After that, keep on going.

gt6_2_project_options

Finally, select a directory to store the project and click Create.

gt6_3_project_create

Step 2

Now you need to add the class files that were implemented in the previous tutorial. This class is going to be the mechanism that will do all the work behind the scenes. So, if you have the previous tutorial's project files, get these two:

  • GoogleOAuth.h
  • GoogleOAuth.m

Add them into your project. If you don't have the previous project, then you can download and get them from this post's download.

gt6_4_class_files

2. Building the Interface

We will use Interface Builder to setup the interface. As you will soon find out, several subviews are going to be added because we want to make the demo app as functional as it can be. Here is a series of steps that describe every subview you should add, along with the properties for each one:

  1. Open the ViewController.xib file to configure the interface and to add all the necessary subviews. First of all, set the view's Size to None in the Utilities Pane > Attributes Inspector > Simulated Metrics, in order to let the project work properly on iPhones prior to the 5.
    gt6_5_size_none
  2. Set the view's Background Color to White.
  3. Drag-and-drop a UIToolbar subview into the view. Place it at the bottom of the screen.
  4. Add the following items on the toolbar (ordered left-to-right):
    • Bar Button Item with Title: Sign out
    • Flexible Space Bar Button Item
    • Bar Button Item with Title: Post
  5. Add a UITableView subview on the view and let it occupy all the available space left on the view.
  6. Set the next two properties of the table view:
    • Style: Grouped
    • Background:: Clear Color

The following is what you should have at this point:

gt6_6_ib_sample

Next, we need to have another view that will contain the date picker view. Here are the steps:

  1. Add a new view outside of the default view and set its Size to None (like you did before). Also, set its Height to 460.
  2. Set the view's Background Color to Scroll View Textured Background Color.
  3. Add a UIDatePicker subview in the view and center it in accordance to the view's center.
  4. Add a UIToolBar subview at the bottom of the view.
  5. Add the following bar button items to the toolbar:
    • Bar Button Item with Title: Cancel
    • Flexible Space Bar Button Item
    • Bar Button Item with Title: All-day event
    • Flexible Space Bar Button Item
    • Bar Button Item with Title: Okay
gt6_7_ib_datepicker

Finally, add the following subviews outside the default view controller's view and in the second view we just created:

  1. A UIToolBar subview. This is going to be the Input Accessory View for the textfield that will be used to edit the event description. Add the next bar button items to it:
    • Bar Button Item with Title: Cancel
    • Flexible Space Bar Button Item
    • Bar Button Item with Title: Okay
  2. A UIActivityIndicatorView with the following properties:
    • Style: Large White
    • Background: Black Color
gt6_8_ib_iav
gt6_9_ib_activityindicator

3. Setup IBOutlet Properties & IBAction Methods

Step 1

We are going to need a few IBOutlet properties connected to our subviews, so we can modify them in code. To connect an IBOutlet property to a subview (as well as to create an IBAction method), you need to have the ViewController.h file shown in the Assistant Editor. So, click on the middle button of the Xcode Editor toolbar to let it show up. Make sure that the contents of the ViewController.h file are displayed there.

gt6_10_assistant_editor

I will show you how to create an IBOutlet property for the table view only. Use the same way to create the properties for the subviews I'll tell you about next.

Either on the Document Outline pane or directly on the view, do the following:

  1. Right-Click or Control-Click on the table view
  2. On the black popup menu, click on the circle next to the New Referencing Outlet option and keep the mouse button pressed.
  3. Drag-and-drop (and at the same time a blue line will follow your mouse) into the Assistant Editor window.
gt6_11_iboutlet_create

On the new window that appears, add the tblPostData as the Name of the property and don't touch any other options. Click on the Connect button or hit the Return button on your keyboard.

gt6_12_iboutlet_name

Here is a list with all the subviews that we need IBOutlet connections created, along with their names. Make sure to follow the same way as before and you'll be fine.

  • Post bar button item: barItemPost
  • Sign out bar button item: barItemRevokeAccess
  • Input Accessory View Toolbar (the alone toolbar): toolbarInputAccessoryView
  • View container of the date picker: viewDatePicker
  • Date Picker: dpDatePicker
  • All-day bar button item: barItemToggleDatePicker
  • Activity Indicator View: activityIndicatorView

Step 2

As you see, we added some bar button items in our views. We require from them to react on our taps, so we need to create IBAction methods to make that happen.

Creating an IBAction method is almost the same as I previously demonstrated. For the Post bar button item only, here is the procedure in detail:

  1. On either the Document Outline or directly on the view, right-click or control-click on the bar button item.
  2. On the black popup menu, under the Sent Actions section, click on the circle next to the Selector option and keep the mouse button down.
  3. Drag and drop on the Assistant Editor window.
gt6_13_ibaction_create

On the new window, add the post in the Name field and leave everything else as it is. Click on Connect or hit the Return button on your keyboard.

gt6_14_ibaction_name

Now, follow the same pattern and create the next IBAction methods for the given subviews:

  • Sign out bar button item: revokeAccess
  • Okay bar button item on the Input Accessory View Toolbar: acceptEditingEvent
  • Cancel bar button item on the Input Accessory View Toolbar: cancelEditingEvent
  • Okay bar button item on the Date Picker View Toolbar: acceptSelectedDate
  • Cancel bar button item on the Date Picker View Toolbar: cancelPickingDate
  • All-day bar button item on the Date Picker View Toolbar: toggleDatePicker

After the addition of all the IBOutlet properties and all the IBAction methods, your ViewController.h file should look like this:


4. Adopting Protocols

Step 1

Now that the interface has been setup and configured and the IBOutlet properties along with the IBAction methods have been created and connected, it's time to start writing some code. Previously we added the GoogleOAuth header and implementation files in the project, but that's not enough to make our class work. We also need to import it in the view controller's class and adopt its protocol.

Open the ViewController.h file and import the GoogleOAuth.h file at the top of the file:

Step 2

In addition to the GoogleOAuth class' protocol, we need to adopt the following as well:

  • UITableViewDelegate: the delegate of the table view.
  • UITableViewDatasource: the datasource of the table view.
  • UITextFieldDelegate: the delegate of the textfield that will be used to edit the event description.

So, while still within the ViewController.h file, modify the @interface statement like this:


5. Declaring Private Properties & Methods

Step 1

There are some properties and some methods that should be declared at the private section of the class and are required to make everything work smoothly. These properties are mostly going to store application information, but some simple flags will also be used to indicate program state.

The next code snippet presents all the private data members you should add in your project (copy and paste them if you'd like). The comments explain everything. Don't forget that we are working now in the ViewController.m file!

Beyond that, add the next method declarations after the properties and before the @end statement:

Step 2

After having declared all these properties, let's do some initialization. This will take place on the viewDidLoad: method. Note that in this method we also set self as the delegate and the datasource of the table view.


6. Table View Datasource & Delegate Methods

Let's keep going by writing some datasource and delegate methods related to the table view. For the time being, we'll add only some standard code. As we move on, we'll add even more code when it's required. So, let's go ahead. As I said at the beginning, there are going to be three sections on the table view:

Each section is going to contain one row. For the third section, we'll have as many rows as the calendars support. In this method, you can see how the _isCalendarListExpanded flag is used for first time:

Let's add some footer titles, in order to make our demo app more descriptive. Nothing hard here:

Set the height of the row for each cell:

Regarding the tableView:cellForRowAtIndexPath: datasource method, this one is going to be built step-by-step while we add more functionalities to the app. For now, let's write only the basics:

Finally, we have only one delegate method, the well-known tableView:didSelectRowAtIndexPath:. Just like the previous one, this is also going to be built step-by-step. For now, make it only remove the selection from each row that is tapped:


7. Editing the Event Description

Step 1

Until now, I have already said many times that a textfield is going to appear on the table view every time that we want to change the event description. However, when not editing, the textfield should not appear. After we finish editing, we'll update the event description and make the textfield go away.

Before we bring this behavior to life, it might be better to implement the private method that we have declared, the setupEventTextfield. In this method, we'll do the following tasks:

  • We will initialize the textfield by setting a frame related to the cell content view's frame and set a style too.
  • We'll set the contents of the strEvent string as its text.
  • Remember the (alone) toolbar we added in the Interface Builder earlier? We'll set it as the input accessory view of the textfield.
  • We'll set self as its delegate so we can handle the Return key of the keyboard.

Let's see it:

Step 2

Now that this method is ready and we can call it any time we'd like to initialize the textfield, let's see how we can implement the behavior we expect from the table view. We want to display the textfield every time that we tap on the row of the first section, so let's do so.

Inside the tableView:didSelectRowAtIndexPath: we'll check if the event is currently being edited. If not, then we'll call the previously implemented method to initialize the textfield, we'll change the flag status indicating whether the event is being edited and we'll show the keyboard.

Step 3

Now let's update the tableView:cellForRowAtIndexPath: so it reflects the state of the first section at any given time:

Great! If you run the app now, you'll notice that the textfield appears on the table view when you tap on the row of the first section. However, you cannot make the textfield vanish and keep the edited value. Why? Because we need to implement the related IBAction methods of the input accessory view bar button items.

Step 4

Let's begin with the acceptEditingEvent: IBAction method. In short, in this method we'll keep the typed event description on the strEvent string, we'll change the isEditingEvent flag's value, we'll make the keyboard disappear, and we'll refresh the table view. Here it is:

The cancelEditingEvent: is similar.

Step 5

In order to be perfectly complete regarding the textfield and the event description editing in general, we have one more thing left to do. Namely, we must handle the Return button of the keyboard and make it work just like the acceptEditingEvent: IBAction method. For this reason, implement the following delegate method:

As you can see, we simply make a call to the IBAction method and nothing more. Now, we are 100% complete regarding the event editing and the textfield manipulation.


8. Picking an Event Date

Step 1

One part of our demo app is complete. Let's go ahead now and let's fully implement the date picking feature. In this case, what we want is when we tap on the row of the second section on the table view, to show the view that contains the date picker and through it to select a date. After that, we want the picked date to be displayed on the table view as well. Special care should be given to the fact that an event can be set for a specific time, but it can also be an all-day event, which means that picking a time has no point at all. We'll see all this next.

Let's begin by showing the date picker container view to the self.view. It's just a matter of one line, which should be written in the tableView:didSelectRowAtIndexPath: delegate method, under any other content it has until now:

Step 2

Okay, that was the easy part. If you run the app now and you tap on the row of the second section, the date picker container view will be displayed on the screen. You'll notice that by default, both date and time are represented in the picker. That's nice if we want to set a specific time for the event. However, we should not let the time appear on the date picker if we are talking about an all-day event. Therefore, we need to implement the toggleDatePicker: IBAction method.

The way this method is going to work is fairly simple. Depending on the current date picker's contents, we will set its mode and we'll also change the respective bar button item's title. Don't forget that there is a flag as well, the isFullDayEvent variable, that should be changed accordingly. The following is the implementation:

Give it another try now and tap (or click on the Simulator) on the All-day event bar button item. Notice how the contents of the date picker get changed, along with the title of the button.

Step 3

Now we know when an event is set as a full-day event, but we are still unable to keep the selected date and to make it appear on the table view. Similarly, we cannot yet cancel the date picking and go back to our view.

So, let's work on both of these IBAction methods now.

In the acceptSelectedDate: IBAction method we need to do only four things: (1) keep the selected date as a string, (2) store the date as a NSDate object, (3) remove the view from the superview, and, finally, (4) reload our table view in order to show the selected date.

In the cancelPickingDate: IBAction method, we only need to remove the date picker container view from the superview.

Step 4

In the acceptSelectedDate: method, we made a call to the getStringFromDate: private method, which is declared but not yet implemented. It's now time to work with this method. Before I present the code, I should make an observation.

The purpose of the getStringFromDate: method is to get the date we provide (the selected date in our example) and to return this date as a string and formatted the way we want. However, there are two kind of string formats we need to have, depending on whether the event is a full-day one or not. If the event date has a specific time, we want this time to be present on the string. This means that we'll have a condition in our method that will determine what the output string format will be. So, having made our intention clear, enter the following code:

The above will give us something like Mon, Aug 12, 2013, 17:32.

For more information about the date symbols used here and any other symbols that exist, look at the Date Format Patterns.

Note: Keep in mind that you should change the date symbol order in a real app in order to match the date representation of your own country.

Step 5

So far so good: just one thing left to do. Add some code on the tableView:cellForRowAtIndexPath: method so the selected date will be displayed. Under all of the other contents of the method, add the next snippet:


9. App Authorization

If you have already run the app, or if you do it now, you'll notice that the row of the third section on the table view contains the Download calendars... message. Of course, nothing takes place when you tap on this method. What we'd like to do on tap is to make an API call to Google and request the information we need. But, prior to this, we must authorize ourselves against the Google service and obtain an access token that will be used to exchange data. Actually, all of this will be done by the GoogleOAuth class we included early on. All wee need to do is provide this class with the client ID, the client secret, and the scope. So, let's pay a visit to the Google developers website and get all the values we need.

Step 1

Go to the Google Developer website. Click on the Sign In button that exists on the top-right side of the webpage to login.

gt6_15_sign_in
gt6_16_sign_in_2

After you have signed in, scroll down on the page until you locate the API Console icon.

gt6_17_api_console

Click on it and you'll be transferred to your Dashboard, where you handle all of your projects. If you must, create a project now, otherwise select the project you created from the previous tutorial in this series. On the menu at the left side of the webpage, click on the API Access option.

gt6_18_api_access_option

Details about the current project will be displayed on the right side of the page. In there, you can track down the client ID and client secret values that you will need. Note them, and let's move ahead.

gt6_19_client_info

Next, we need to tell Google that we want to use the Calendars service. To do so, click on the Services option at the menu on the left side of the webpage. A list of all the provided services will appear. Locate the Calendar API item. Click on the Off button to enable the specific service for the current project.

gt6_20_services

Step 2

What we haven't located yet is the scope value for getting the calendar info we need. Just to remind you, a scope indicates an API that the app requests access for. The best practice to locate what you want is to use the search engine for the Google developer website. So, go to the homepage of the Google developer site and search for the term Google Calendar API. In the results page, click on the first result.

gt6_22_search_box

ake a look around if you want and explore using the menus. For this tutorial, you should go to Reference > Calendar List > list. A new webpage is loaded, containing information about the calendar list API call. Find the Authorization area near the bottom of the page. There you can find the scope we need: https://www.googleapis.com/auth/calendar.

gt6_21_scopes_calendarlist

Step 3

In the previous steps, I showed you how you can enable a service on Google and the best way to find what you need from the developer web site. Now, we can keep building the app.

Now that we have access to all the data we need from Google, let's get back to our app and see how to use the information. Append the following code to our project:

You'll notice that above we display the indicator view as well. We do this because we don't know how long it'll take to obtain authorization. Don't forget to set your own values for the client ID and the client secret!

When you use the app for the first time, the embedded web view will appear. You must enter your credentials and sign into your Google account to allow the app to access your calendars. If everything goes okay, you'll be authorized without a problem.


10. Downloading Calendar Data

Step 1

At this point, we need to implement some of the GoogleOAuth delegate methods. Let's begin with the authorizationWasSuccessful method, where we'll handle a successful authorization by making the API call for getting the calendar list.

However, we need the API URL string if we want to proceed. Navigate to the proper calendar list page in your Google developer account. At the top of the page you'll find the URL along with the HTTP method that should be used.

gt6_23_api_calendarlist

Now we can implement the delegate method:

Step 2

If all works according to plan, the responseFromServiceWasReceived:andResponseJSONAsData: delegate method will be called by the GoogleOAuth class. We are responsible to check if Google responded with the desired results, and then to keep only the data that we care about.

Let's discuss a bit about what the response contains and how we are going to manage the data. What we should do first is to convert the response JSON data into an NSDictionary object. If you NSLog this dictionary, we'll see the way the returned data is formed. See the following example:

Inside each curly bracket there is a block containing a bunch of information regarding every calendar you have created in Google Calendars. The items object is equivalent to an array which contains dictionaries as objects. In other words, we will extract the items object as a NSArray and we'll handle every single object of it as a NSDictionary object.

Let's get back on track again. Once we acquire all the items as NSArray objects, we'll go through a loop to access each calendar's details and we'll keep only the values we want for the purposes of this example. Actually, we are going to create key-value pairs with these values with the goal of creating new NSDictionaries, which will be stored in the arrGoogleCalendars array. This array is the calendar list for our app. Also, the dictCurrentCalendar dictionary will be initialized with the contents of the first calendar from the list. Once this has been done, the Post and Logout items will become enabled. We'll also hide the activity indicator view and we'll refresh the table to show the selected calendar.

Here is the code:

Step 3

Now that we've done all the above, we want to be able to tap on a calendar name and have the full list expand, allowing us to select another calendar. This will be done in the tableView:didSelectRowAtIndexPath: method. Add the following code snippet:

Step 4

Finally, we need to update the tableView:cellForRowAtIndexPath: method to display everything we've done. Append the following code:

That's it. Go and give it a try. Watch your calendars on the Simulator and play around for a while by expanding the calendar list and selecting a calendar!


11. Posting Calendar Events

Step 1

Let's see how we'll manage to add an event to a selected calendar. The first thing we should do is make sure that the event description and the event date have been set. To verify this, we'll check their values and we'll show an alert if something is wrong.

Our work will now take place in the post: IBAction method.

You'll notice that we'll simply check if the event description string is equal to the empty string and if the event date string is equal to its initial value.

Step 2

As I have already said, what we actually do in this project is to implement the Quick-add feature that Google Calendar supports. However, we need to know the format for the event string because there are some rules that apply. For example, there are special ways that the event date and time should be appended at the end of the event description string, so Google will know the exact date/time of the event. Of course, if you have already used the online Quick-Add feature, then you surely know what I am talking about.

Thankfully, Google provides help and examples regarding this issue. We just have to visit the Quick Add documentation. Go and visit this website and familiarize your self with this feature.

In our case, if we have a full-day event, we simply have to add the date using slashes at the end of the event description string (for example, "This is an event 08/12/2013"). If we don't have a full-day event, then we will add the time too, using the "at" between the date and time (for example, "This is an event 08/12/2013 at 21:40").

In addition, we need to know the URL of the API we want to call. Just like we with the calendar list, we must find the quick-add related information in the Google Calendar API documentation. If you don't want to bother looking for it right now, this is where you can find it. We must search for the following information:

  • Request: This is the URL of the API we want to call as it appeared at the top of the page. Also notice that we need to use the POST HTTP method.
  • POST parameters: Here are the parameters we need to send with the POST method. There are only two mandatory params, the ID of the calendar on which we want to add the event and the event text (of course formatted with the date, as I indicated before).
  • Authorization: This is the scope that we should be authorizing. In this case, the scope is equal to the calendar list scope, so we don't need to care about it. However, if that was a different value, then we should include it in the scope array during authorization.

So, let's go back to our method to finish things. Update our code as follows:

If you run the app now and you click on the Post button, the event will be posted. You can verify this if you check your calendar from a web browser. However, until we handle the response from Google, we are unable to know if the event was successfully added or not.

Step 3

Each time that an event is posted, Google creates a respective object which contains several properties and data. After a successful addition, Google responds with this data, so we can have any information we need regarding the newly created event. In our case, we won't do something extraordinary. We'll simply show an alert view that will contain the following values, so we are sure that the event was successfully posted.

  • ID: Each new event gets a unique ID value, which we are going to display on the alert view.
  • Created Date: The date that the event was created.
  • Summary: The event description itself.

Of course, in a real app, you must handle this event data in a different way. Always regard the app's needs.

We are going to work in the responseFromServiceWasReceived:andResponseJSONAsData: method again. Notice that the JSON data is converted again into an NSDictionary object.

Now, every time you add an event an alert view containing the new event info is displayed.


12. Signing Out

Signing out of the Google account is an option that should always be provided to the users, even though it's recommended to keep them connected for faster access to online services. For our app, it's only a matter of one single line of code, which we'll add on the revokeAccess: IBAction method.

The revokeAccessToken method will make a call to the accessTokenWasRevoked delegate method to inform our class that the access token was revoked. This is going to be implemented next.


13. Ancillary Methods

Step 1

Until now, we used only two delegate methods of the GoogleOAuth class, the authorizationWasSuccessful and the responseFromServiceWasReceived:andResponseJSONAsData:. There are three more of them that we should implement. We need a delegate method for handling the access revocation, another delegate for handling any error that may occur, and a final method for dealing with any error messages that may exist in Google responses.

Regarding the access revocation, we'll have to do only three things. First, we need to remove all calendars from the arrGoogleCalendars array. Next, we need to disable the Post and the Sign Out buttons. Finally, we need to reload the table view to keep it up to date.

For the next two error handling delegate methods, we won't do much. We'll simply log the error messages and nothing further. It's obvious that in a real app you would have to handle the errors in an appropriate way and figure out workarounds that handle unexpected situations. For now, here are our stub implementations:

Step 2

During the project implementation, we made a few calls to the showOrHideActivityIndicatorView private method, which has been declared and called but not yet built. Let's deal with it now.

That was the last thing we had to do. Finally, our app is finished! Try out all the functionality supported.


Conclusion

In this tutorial I demonstrated how to work with Google Calendars using OAuth 2.0. While implementing a web service in-app is a lot of work, I hope you find the effort to have been worthwhile! Thanks for reading, and feel free to leave any questions or feedback in the comments below!

Tags:

Comments

Related Articles