Add Ins Connectors REST APIs Actionable Messages Feedback Blog Code Samples Videos

Write an iOS app to get Outlook mail

The purpose of this guide is to walk through the process of creating a simple Swift app that retrieves messages in Office 365. The source code in this repository is what you should end up with if you follow the steps outlined here.

Before you begin

This guide assumes that you already have Xcode installed and working on your development machine, along with CocoaPods.

We have released a new SDK, the Outlook SDK for iOS, which includes the functionality for the v2 updates to the Outlook REST APIs. This tutorial uses the Office 365 SDKs for iOS, which uses the v1 versions of the Outlook APIs. We are working on updating this tutorial.

This tutorial uses the v1 Azure OAuth2 endpoints and is not compatible with Outlook.com. We will udpate this tutorial once the iOS ADAL library is updated for compatibilty with the v2 Azure OAuth2 endpoints.


1. Create the app

Let's dive right in! Open Xcode, and on the File menu, choose New then Project. In the left-hand pane, choose Application under iOS, then choose Single View Application. Click Next.

Select the Single View Application template.

Enter swift-tutorial for Product Name, Swift for Language, and Universal for Devices, then click Next.

Choose options for your new project.

Choose a location for the project and click Create. Once Xcode finishes creating the project, close Xcode.

Next, use CocoaPods to install dependencies. For this tutorial, we will need the following:

Open Terminal and change the directory to the location of your swift-tutorial project. Run the following command to initialize a Podfile for the project.

pod init

Next, open the Podfile using the following command.

open Podfile

Replace the entire contents of the file with:

use_frameworks!

target 'swift-tutorial' do
    pod 'ADALiOS'
    pod 'Office365/Outlook'
end

target 'swift-tutorialTests' do

end

target 'swift-tutorialUITests' do

end 

Close the Podfile, then run the following command to install the dependencies.

pod install

In Pods/Pods/ADALiOS/Support Files/ADALiOS-umbrella.h, remove #import "ADPersistentTokenCacheStore.h". Delete ADPersistentTokenCacheStore.h from Pods/Pods/ADALiOS.

Once that command finishes, open the newly created swift-tutorial.xcworkspace file in Xcode.

Finally, add a bridging header to enable use of the dependencies from Swift code. In the Project navigator, expand swift-tutorial and select the swift-tutorial folder underneath it. On the File menu, choose New then File. Select Header file as the template and click Next. Enter bridging in the Save As field and click Create.

Open the bridging.h file and add the following lines

#import <ADALiOS/ADAL.h>
#import <orc/orc.h>
#import <outlook_services/outlook_services.h>

Select the swift-tutorial project in the Project navigator, then select the Build Settings tab. In the Swift Compiler - Code Generation section, set the value of the Objective-C Bridging Header setting to bridging.h.

TODO: UPDATE IMAGE Configure a bridging header in Build Settings.


2. Designing the app

The app itself will be fairly simple. Since it's a single-page app, we'll keep it to a minimum.

In the Project navigator, expand swift-tutorial->swift-tutorial, then select Main.storyboard. In the document outline, expand View Controller Scene, then select View Controller.

TODO: UPDATE IMAGE Select the View Controller in the document outline.

On the Editor menu, choose Embed In, then Navigation Controller. A new item named Navigation Item should appear in the document outline underneath View Controller. Select this item.

In the Object Library (bottom-right corner), find Button. Drag Button onto the visual representation of the view. Double-click the button and set the text to Log In.

Add a Log In button.

Now select ViewController.swift in the Project navigator. Add a new property to the ViewController class, right before the viewDidLoad function:

@IBOutlet var logInButton : UIButton!

Then add a new method to the class:

@IBAction func logInButtonTapped(sender : AnyObject) {
    NSLog("Hello World")
}

In Main.storyboard, select View Controller in the document outline. Select the Connections inspector tab on the right-hand side.

Select the Connections inspector.

Under Outlets, you should see the loginButton property we added to the view controller earlier. Drag the small circle next to this property onto the button on the view.

Under Received Actions, you should see the logInButtonTapped method we added to the view controller earlier. Drag the small circle next to this method onto the button on the view. In the pop up menu that appears, select Touch Up Inside. The Connections inspector section should look like this once you are done.

The Connections inspector with the login button outlet connected.

In Main.storyboard, select View in the document outline. On the Editor menu, select Resolve Auto Layout Issues, then Add Missing Constraints.

At this point the app should build and run. Tapping the Log in button should call the loginButtonTapped method (which at this point does nothing).


3. Register the app

App registrations can be created and managed in the Azure Management Portal. But for this tutorial, let's keep things simple. You can create an app registration with a few simple clicks.

First, you need access to a user account in an Office 365 Subscription, or a Microsoft account.

For Office 365, you can use an existing subscription you have for your work or business, an Office 365 Developer Subscription, or you can start with a free 30-day trial. Just bear in mind that whatever subscription you use to register your app will show as the publisher for that app when users are prompted to give consent.


4. Implementing OAuth2

Our goal in this section is to make the Log in button use OAuth2 to obtain an access token that we can use with the Mail API. We'll start by creating a class to handle our authentication.

CTRL + Click the swift-tutorial folder in Project navigator and select New File. Select Swift file as the template and click Next. Enter AuthenticationManager in the Save As field and click Create.

Open the AuthenticationManager.swift file and replace its contents with the following.

Contents of the Authentication.swift file

import Foundation

class AuthenticationManager {
    // This value must match the value used when registering the app
    let redirectURL = NSURL(string: "http://localhost/swift-tutorial")
    // The Azure OAuth2 authority
    let authority = "https://login.microsoftonline.com/common"
    // The resource identifier for the Outlook APIs
    let outlookResource = "https://outlook.office365.com"
    // The client ID obtained by registering the app
    let clientId = "YOUR CLIENT ID HERE"
    // ADAL dependency resolver, used to enable logon UI
    var dependencyResolver = ADALDependencyResolver()
    
    // Create a singleton instance of the AuthenticationManager
    // class. The app uses this instance for all operations.
    class var sharedInstance: AuthenticationManager {
        struct Singleton {
            static let instance = AuthenticationManager()
        }
        return Singleton.instance
    }
    
    // Asynchronous function to retrieve a token. This will load the
    // token from the cache (if present), otherwise it will use 
    // ADAL to prompt the user to sign into Azure.
    func getToken(completionHandler:((Bool, String) -> Void)) {
        var err: ADAuthenticationError?
        var authContext: ADAuthenticationContext = ADAuthenticationContext(authority: authority,
            error: &err)
        
        // Acquire the token. This will prompt the user to login if there is not
        // a valid token already in the app's cache.
        authContext.acquireTokenWithResource(outlookResource, clientId: clientId, redirectUri: redirectURL) {
            (result: ADAuthenticationResult!) -> Void in
            
            if result.status.value != AD_SUCCEEDED.value {
                // Failed, return error description
                completionHandler(false, result.error.description)
            }
            else {
                // Succeeded, return the acess token
                var token = result.accessToken
                // Initialize the dependency resolver with the logged on context.
                // The dependency resolver is passed to the Outlook library.
                self.dependencyResolver = ADALDependencyResolver(context: authContext, resourceId: self.outlookResource, clientId: self.clientId, redirectUri: self.redirectURL)
                completionHandler(true, token)
            }
        }
    }
    
    // Logout function to clear the app's cache and remove the user's information.
    func logout() {
        var error: ADAuthenticationError?
        var cache: ADTokenCacheStoring = ADAuthenticationSettings.sharedInstance().defaultTokenCacheStore
        
        // Clear the token cache
        var allItemsArray = cache.allItemsWithError(&error)
        if (!allItemsArray.isEmpty) {
            cache.removeAllWithError(&error)
        }
        
        // Remove all the cookies from this application's sandbox. The authorization code is stored in the
        // cookies and ADAL will try to get to access tokens based on auth code in the cookie.
        var cookieStore = NSHTTPCookieStorage.sharedHTTPCookieStorage()
        if let cookies = cookieStore.cookies {
            for cookie in cookies {
                cookieStore.deleteCookie(cookie as! NSHTTPCookie)
            }
        }
    }
}

Replace the value of clientId with the client ID you generated in step 3.

Switch back to ViewController.swift. Add another property to the ViewController class (just below the definition for loginButton:

var loggedIn: Bool = false

Now let's add code to the empty logInButtonTapped function.

@IBAction func logInButtonTapped(sender : AnyObject) {
    var authMgr = AuthenticationManager.sharedInstance
    
    if (loggedIn){
        // Logout and change the button to read "Log in"
        authMgr.logout()
        self.logInButton.setTitle("Log in", forState: UIControlState.Normal)
        self.loggedIn = false
    }
    else {
        // Attempt to get a token
        authMgr.getToken() {
            (authenticated: Bool, token: String) -> Void in
            
            if (authenticated) {
                // Change the button to read "Log out"
                NSLog("Authentication successful, token: %@", token)
                self.logInButton.setTitle("Log out", forState: UIControlState.Normal)
                self.loggedIn = true
            }
            else {
                NSLog("Authentication failed: %@", token)
            }
        }
    }
}

Run the app. Once the app loads in the iOS Simulator, tap the Log in button to login. After logging in, you should see a token printed to the output window, and the button should now say Log out.

The access token displayed in the output window in Xcode.

Copy the entire value of the token and head over to https://jwt.calebb.net/. If you paste that value in, you should see a JSON representation of an access token. For details and alternative parsers, see Validating your Office 365 Access Token.

Once you're convinced that the token is what it should be, let's move on to using the Mail API.


5. Using the Mail API

Switch back to ViewController.swift and add a new function getMessages.

getMessages function in ViewController.swift

func getMessages(dependencyResolver: ADALDependencyResolver) {
    var apiEndpoint = "https://outlook.office365.com/api/v1.0"
    var client = MSOutlookClient(url: apiEndpoint, dependencyResolver: dependencyResolver)
    
    // Select at most 10 messages (.top(10))
    // Return only the subject, date/time received, and from fields (.select())
    // Sort by date/time received, newest first (.orderBy())
    client.me.messages.top(10).select("Subject,DateTimeReceived,From").orderBy("DateTimeReceived DESC").readWithCallback({
        (messages: [AnyObject]!, error: MSOrcError!) -> Void in
        dispatch_async(dispatch_get_main_queue(), {
            () -> Void in
            
            for msg in messages {
                var olMsg: MSOutlookMessage = msg as! MSOutlookMessage
                NSLog(olMsg.Subject)
            }
        })
    })
}

Update the loginButtonTapped function to call getMessages after a successful login.

Updated loginButtonTapped function in ViewController.swift

@IBAction func logInButtonTapped(sender : AnyObject) {
    var authMgr = AuthenticationManager.sharedInstance
    
    if (loggedIn){
        // Logout and change the button to read "Log in"
        authMgr.logout()
        self.logInButton.setTitle("Log in", forState: UIControlState.Normal)
        self.loggedIn = false
    }
    else {
        // Attempt to get a token
        authMgr.getToken() {
            (authenticated: Bool, token: String) -> Void in
            
            if (authenticated) {
                // Change the button to read "Log out"
                NSLog("Authentication successful, token: %@", token)
                self.logInButton.setTitle("Log out", forState: UIControlState.Normal)
                self.loggedIn = true
                
                // Get messages
                // Pass in the ADALDependencyResolve from the AuthenticationManager
                self.getMessages(authMgr.dependencyResolver)
                
            }
            else {
                NSLog("Authentication failed: %@", token)
            }
        }
    }
}

Restart the app. After you tap the Log in button, you should see a list of message subjects appear in the output window.

Message subjects displayed in the output window in Xcode.


6. Displaying the results

Let's add a table view to our app to display the list of messages. First, add two new properties to the ViewController class in ViewController.swift.

@IBOutlet var msgTable: UITableView!
var messages = NSArray()

msgTable is the table view, and messages will hold the list of messages retrieved via the Mail API.

Add the following line to the viewDidLoad function:

self.msgTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")

Next we need to add a couple of protocols to the ViewController class to allow it to interact with and update the table view. Change the following line:

class ViewController: UIViewController {

To this:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

Now add the following functions to the ViewController class to implement the new protocols.

// Called when loading data to see how many rows to render
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of messages
    return self.messages.count
}

// Called to render each row in the table
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell:UITableViewCell = self.msgTable.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
    
    // Get the message from the array and cast to a MSOutlookMessage object
    var outlookMessage : MSOutlookMessage = self.messages.objectAtIndex(indexPath.row) as! MSOutlookMessage
    
    // Format the received date/time
    var formatter = NSDateFormatter()
    formatter.timeStyle = NSDateFormatterStyle.ShortStyle
    formatter.dateStyle = NSDateFormatterStyle.ShortStyle
    var received = formatter.stringFromDate(outlookMessage.DateTimeReceived)
    
    // Setting to 0 should allow the line to wrap to multiple lines
    cell.textLabel?.numberOfLines = 0
    cell.textLabel?.text = String(format: "%@ From: %@ - %@", received, outlookMessage.From.EmailAddress.Name, outlookMessage.Subject)
    
    return cell
}

// Empty function
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    
}

Update the getMessages function to save the returned messages into the messages class property and call reloadData on the table view.

Updated getMessages function in ViewController.swift

func getMessages(dependencyResolver: ADALDependencyResolver) {
    var apiEndpoint = "https://outlook.office365.com/api/v1.0"
    var client = MSOutlookClient(url: apiEndpoint, dependencyResolver: dependencyResolver)
    
    // Select at most 10 messages (.top(10))
    // Return only the subject, date/time received, and from fields (.select())
    // Sort by date/time received, newest first (.orderBy())
    client.me.messages.top(10).select("Subject,DateTimeReceived,From").orderBy("DateTimeReceived DESC").readWithCallback({
        (messages: [AnyObject]!, error: MSOrcError!) -> Void in
        dispatch_async(dispatch_get_main_queue(), {
            () -> Void in
            
            for msg in messages {
                var olMsg: MSOutlookMessage = msg as! MSOutlookMessage
                NSLog(olMsg.Subject)
            }
            
            // Save the results and refresh the table view
            self.messages = messages
            self.msgTable.reloadData()
        })
    })
}

Switch to Main.storyboard. In the Object library, locate Table View. Drag Table View onto the main view controller.

Add a table view to the view in the main storyboard.

Select the main view controller in the document outline. Select the Connections inspector tab on the right-hand side. Under Outlets, you should see msgTable. Drag the small circle next to it onto the table view you added to the main view controller. Repeat for dataSource and delegate under Referencing Outlets. When done, your Connections inspector should look like the following.

Connect the table view to the msgTable variable in the view controller.

Re-run the app. After tapping the Log in button and logging in, you should see a table of messages.

The sample app displaying messages.


Adding Calendar and Contacts APIs

Now that you've mastered calling the Outlook Mail API, doing the same for Calendar and Contacts APIs is similar and easy.

For Calendar API:

Note: this code requires permissions to read users' calendars. If you did not select 'Events' in the app registration step, please create a new registration that includes Events, or modify your existing registration in the Azure Management Portal.

  1. Select Main.storyboard in the Project navigator. Drag a View Controller from the Object library onto the storyboard's canvas.

  2. Drag a Button from the Object library onto the Swift Tutorial view. Double-click the new button and set the text to Calendar. While holding down the CTRL key on the keyboard, drag the new button to the new View Controller you just created. On the menu that pops up, select present modally.

  3. Drag a Button from the Object library onto the new View Controller. Double-click the new button and set the text to Back. While holding down the CTRL key on the keyboard, drag the new button to the Swift Tutorial view controller. On the menu that pops up, select present modally.

  4. In the Project navigator, CTRL + click the swift-tutorial folder and choose New File. Select Swift File (under iOS/Source) and click Next. Enter CalendarViewController in the Save As field and click Create.

  5. Replace the contents of the new file with the following code.

    import UIKit
    
    class CalendarViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
        @IBOutlet var eventsTable: UITableView!
        var events = NSArray()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            self.eventsTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
            getEvents(AuthenticationManager.sharedInstance.dependencyResolver)
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        func getEvents(dependencyResolver: ADALDependencyResolver) {
            var apiEndpoint = "https://outlook.office365.com/api/v1.0"
            var client = MSOutlookClient(url: apiEndpoint, dependencyResolver: dependencyResolver)
    
            // Select at most 10 events (.top(10))
            // Return only the subject, start, and end (.select())
            // Sort by start, earliest date first (.orderBy())
            client.me.events.top(10).select("Subject,Start,End").orderBy("Start ASC").readWithCallback({
                (events: [AnyObject]!, error: MSOrcError!) -> Void in
                dispatch_async(dispatch_get_main_queue(), {
                    () -> Void in
    
                    for evt in events {
                        var olEvent: MSOutlookEvent = evt as! MSOutlookEvent
                        NSLog(olEvent.Subject)
                    }
    
                    // Save the results and refresh the table view
                    self.events = events
                    self.eventsTable.reloadData()
                })
            })
        }
    
        // Called when loading data to see how many rows to render
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // Return the number of events
            return self.events.count
        }
    
        // Called to render each row in the table
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            var cell:UITableViewCell = self.eventsTable.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
    
            // Get the message from the array and cast to a MSOutlookEvent object
            var outlookEvent : MSOutlookEvent = self.events.objectAtIndex(indexPath.row) as! MSOutlookEvent
    
            // Format the start and end date/time
            var formatter = NSDateFormatter()
            formatter.timeStyle = NSDateFormatterStyle.ShortStyle
            formatter.dateStyle = NSDateFormatterStyle.ShortStyle
            var start = formatter.stringFromDate(outlookEvent.Start)
            var end = formatter.stringFromDate(outlookEvent.End)
    
            // Setting to 0 should allow the line to wrap to multiple lines
            cell.textLabel?.numberOfLines = 0
            cell.textLabel?.text = String(format: "%@ Start: %@ End: %@", outlookEvent.Subject, start, end)
    
            return cell
        }
    
        // Empty function
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    
        }
    }
    
  6. Switch back to Main.storyboard. Select the new view controller in the document outline. On the right-hand side, select the Identity inspector. Change the Class field to CalendarViewController.

  7. Drag a Table View from the Object library onto the new view controller.

  8. Select the new view controller in the document outline. Select the Connections inspector on the right-hand side. Drag the small circle next to eventsTable to the table view you just added.

  9. Select the table view on your new view controller. In the Connections inspector, drag the small circle next to dataSource to the view controller. Repeat for delegate.

  10. Run the app. After logging in, tap the Calendar button.

For Contacts API:

Note: this code requires permissions to read users' calendars. If you did not select 'Contacts' in the app registration step, please create a new registration that includes Contacts, or modify your existing registration in the Azure Management Portal.

  1. Select Main.storyboard in the Project navigator. Drag a View Controller from the Object library onto the storyboard's canvas.

  2. Drag a Button from the Object library onto the Swift Tutorial view. Double-click the new button and set the text to Contacts. While holding down the CTRL key on the keyboard, drag the new button to the new View Controller you just created. On the menu that pops up, select present modally.

  3. Drag a Button from the Object library onto the new View Controller. Double-click the new button and set the text to Back. While holding down the CTRL key on the keyboard, drag the new button to the Swift Tutorial view controller. On the menu that pops up, select present modally.

  4. In the Project navigator, CTRL + click the swift-tutorial folder and choose New File. Select Swift File (under iOS/Source) and click Next. Enter ContactsViewController in the Save As field and click Create.

  5. Replace the contents of the new file with the following code.

    import UIKit
    
    class ContactsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
        @IBOutlet var contactsTable: UITableView!
        var contacts = NSArray()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            self.contactsTable.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
            getContacts(AuthenticationManager.sharedInstance.dependencyResolver)
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        func getContacts(dependencyResolver: ADALDependencyResolver) {
            var apiEndpoint = "https://outlook.office365.com/api/v1.0"
            var client = MSOutlookClient(url: apiEndpoint, dependencyResolver: dependencyResolver)
    
            // Select at most 10 contacts (.top(10))
            // Return only the first name, last name, and email addresses (.select())
            // Sort by first name, A to Z (.orderBy())
            client.me.contacts.top(10).select("GivenName,Surname,EmailAddresses").orderBy("GivenName ASC").readWithCallback({
                (contacts: [AnyObject]!, error: MSOrcError!) -> Void in
                dispatch_async(dispatch_get_main_queue(), {
                    () -> Void in
    
                    for contact in contacts {
                        var olContact: MSOutlookContact = contact as! MSOutlookContact
                        if (olContact.GivenName != nil) {
                            NSLog(olContact.GivenName)
                        }
                        else {
                            NSLog("Contact w/o GiveName set")
                        }
                    }
    
                    // Save the results and refresh the table view
                    self.contacts = contacts
                    self.contactsTable.reloadData()
                })
            })
        }
    
        // Called when loading data to see how many rows to render
        func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            // Return the number of contacts
            return self.contacts.count
        }
    
        // Called to render each row in the table
        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            var cell:UITableViewCell = self.contactsTable.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
    
            // Get the contact from the array and cast to a MSOutlookContact object
            var outlookContact : MSOutlookContact = self.contacts.objectAtIndex(indexPath.row) as! MSOutlookContact
    
            var primaryEmail: MSOutlookEmailAddress = outlookContact.EmailAddresses.objectAtIndex(0) as! MSOutlookEmailAddress
            var firstName = "No First Name"
            if (outlookContact.GivenName != nil) {
                firstName = outlookContact.GivenName
            }
    
            var lastName = "No Last Name"
            if (outlookContact.Surname != nil) {
                lastName = outlookContact.Surname
            }
            // Setting to 0 should allow the line to wrap to multiple lines
            cell.textLabel?.numberOfLines = 0
            cell.textLabel?.text = String(format: "%@ %@ <%@>", firstName, lastName, primaryEmail.Address)
    
            return cell
        }
    
        // Empty function
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    
        }
    }
    
  6. Switch back to Main.storyboard. Select the new view controller in the document outline. On the right-hand side, select the Identity inspector. Change the Class field to ContactsViewController.

  7. Drag a Table View from the Object library onto the new view controller.

  8. Select the new view controller in the document outline. Select the Connections inspector on the right-hand side. Drag the small circle next to contactsTable to the table view you just added.

  9. Select the table view on your new view controller. In the Connections inspector, drag the small circle next to dataSource to the view controller. Repeat for delegate.

  10. Run the app. After logging in, tap the Contacts button.