How To Make Gmail iOS App in Swift

Gmail is my favorite email application. It’s grown massively over the years and is the default for many users. I’ve used it personally and at many different companies.

For a lot of people, its one of the first screens you see every morning for work.

Let’s see how they build their main view:


Getting Started

I recommend downloading the starter project here or you can create a new project from blank in Xcode. If you start your own project from blank in Xcode, read this article on how to remove storyboards.

Change the bundle identifier to something unique.

Next select your preferred development team (personal for most people).


Run and build your app, it will look like this:


This app currently doesn’t do anything. We’re going to be building each of the controls step by step.

User Interface Elements


I’ve drawn a red box around most of the main UI elements that we’ll build in this article. The other elements will be covered in another article.

Table View

From looking at the Gmail application, the main view appears to be one large table view. Let’s start by building that. Open up your MainViewController.xib.

Here we’ll add a table view by open up the Object Library by either using the keyboard shortcut CMD + Shift + L or by clicking the square within a circle in the top right corner.

Once you have that dialog open, search for table.


Drag the table view into your view and expand it so the corners fill the view.


Now add constraints to the table view so that it is pinned to the superview. To do this, select the table view and the click the tie-fighter icon in the bottom right. Click on all four constraints and set them to zero.


This way your table view will be pinned to the edges, no matter what screen size it is on.

Go into your main view controller and add an IBOutlet for your table view now. To do this click on the table view, hold ctrl, click and drag it into your Swift file.


@IBOutlet weak var tableView: UITableView!

Now create a viewDidLoad function:

override func viewDidLoad() {
    tableView.dataSource = self
    tableView.delegate = self
    tableView.separatorStyle = .none

Xcode will give you an error here and demand that this class be of type UITableViewDataSource and UITableViewDelegate. So create an extension just underneath this class to fulfill these requirements.

extension MainViewController: UITableViewDataSource, UITableViewDelegate {

Let’s leave this extension empty for now.

Table Cell

The next step is to create a custom table cell for your table view.

Create a new Cocoa Touch Class by going to File > New File or with keyboard shortcut CMD+N. Select the Cocoa Touch Class:


Be sure to subclass UITableViewCell and check Also Create Xib.


Open up your table view cell xib and search for label in your object library. You can open up the object library by clicking the square within a circle icon in the top right. Or by using keyboard short CMD + Shift + L.

Position your first label like so:


This will be the from address line. Here are my font settings that you can access when you click the Attribute Inspector (CMD + Option + 4).


Next create another label for the email subject line.


Here are my settings for this label:


Lastly create a label for an excerpt of the email:


These are my settings for the email excerpt, I am using the color #7B7B7B for the text:


Lastly we have to add the circle icon we see in Gmail. To do this, we’ll create an image view and drag it into place. Grab an image view from the object library (CMD + Shift + L) and put it to the left of the text.



Now let’s add constraints to lock all these positions in place. To add constraints click on an element, hold CTRL and then drag to another element.


For the sender line (“Credit King Tax”), I added these constraints.


You can view all constraints on an element by selecting it and then opening its Size Inspector (keyboard shortcut: CMD+Option+5) For the subject line (“Get Ready For Tax Season”), I added these constraints:


For the email excerpt line (“File your taxes for free”) I added these constraints:


For the image view I added these constraints:


To add height and width constraints, remember to click the tie-fighter icon in the bottom right hand corner.


Profile Image

For the image, I’ve made a circle image in Photoshop to paste in here.


Add this to your Assets.xcassets and set your image to this.

So all together our table cell should look like this in the XIB:


Table Cell Height

We need to set our cell height correctly. Go to the Size Inspector of your table cell view and set it to 75.


Now we’ll have to set the same height in our table view. Open up MainViewController.xib, select the table view and go to the Size Inspector. Set the cell height to the same value 75.


Model And More Emails

It’s nice that we have one email, but what do we do when we have multiple emails? Let’s create an email model swift file. A model is a representation of the information required to build an email cell.

Create a Swift new file (CMD + N) and name it EmailModel.swift. In this empty file, we’ll create a class named EmailModel.

class EmailModel {


Let’s think about what properties we want this class to have. We know all of our emails have the following:

  • Sender
  • Sender Profile Image
  • Subject Line
  • Email Excerpt
  • Starred or not

So let’s add these elements to our class:

import Foundation
import UIKit

class EmailModel {
    let sender: String
    let senderProfileImage: UIImage
    let subjectLine: String
    let emailExcerpt: String
    var starred: Bool

I also had to add import UIKit since the type UIImage is in there. You’ll notice I made most of the properties constants with the let keyword. I made starred a variable, since the user can star and unstar emails.

Now let’s create a constructor for this class:

init(sender: String, senderProfileImage: UIImage, subjectLine: String, emailExcerpt: String, starred: Bool) {
    self.sender = sender
    self.senderProfileImage = senderProfileImage
    self.subjectLine = subjectLine
    self.emailExcerpt = emailExcerpt
    self.starred = starred

In our main view controller we can create some email model instances:

let emails = [
    EmailModel(sender: "The Tax Man", senderProfileImage: UIImage(named: "person1")!, subjectLine: "Time to file taxes!", emailExcerpt: "Completely free", starred: true),
    EmailModel(sender: "Cool Gym", senderProfileImage: UIImage(named: "person2")!, subjectLine: "Awesome gym deal!", emailExcerpt: "No sign up fee this week", starred: false),
    EmailModel(sender: "Discount Clothes", senderProfileImage: UIImage(named: "person3")!, subjectLine: "New summer styles", emailExcerpt: "50% off everything!", starred: false),

Now we three demo emails to test our app. In a later tutorial, we’ll learn how to pull this information from a backend database.

Loading Emails Into User Interface

So how do we get these emails into our user interface?

Open up EmailTableViewCell and clear it out.

import UIKit

class EmailTableViewCell: UITableViewCell {


Now open up the EmailTableViewCell.xib in your assistant editor by holding Option and clicking on it.


Now we’re going to add multiple IBOutlets to our EmailTableViewCell.swift. To do this, hold ctrl, then click and drag each of the elements over.


You should have all the elements as IBOutlets in your Swift file now:

class EmailTableViewCell: UITableViewCell {
    @IBOutlet weak var profileImageView: UIImageView!
    @IBOutlet weak var sender: UILabel!
    @IBOutlet weak var subjectLine: UILabel!
    @IBOutlet weak var emailExcerpt: UILabel!

Let’s also add a reference to the EmailModel as a property of this view cell.

var emailModel: EmailModel?

Now let’s create a function that allows us to set these up using an EmailModel.

func setEmailModel(emailModel: EmailModel) {
    self.emailModel = emailModel

Modify our emailModel property to set up the view properly when it’s set:

var emailModel: EmailModel? {
    didSet {
        guard let emailModel = emailModel else { return }
        senderProfileImage.image = emailModel.senderProfileImage
        sender.text = emailModel.sender
        subjectLine.text = emailModel.subjectLine
        emailExcerpt.text = emailModel.emailExcerpt

Awesome - that’s it for our EmailTableViewCell.swift.

Let’s go back to our main view controller and setup the table view correctly.

Fill in our extension:

extension MainViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return emails.count

This tells our table view how many rows it will have. We set it to number of emails in the emails array.

Next we’ll build each cell:

extension MainViewController: UITableViewDataSource, UITableViewDelegate {

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = Bundle.main.loadNibNamed("EmailTableViewCell", owner: self, options: nil)![0] as! EmailTableViewCell
        cell.setEmailModel(emailModel: emails[indexPath.row])
        return cell

This returns our custom cell set with the proper email model for each row. We first load it from it’s Nib, then set the model. Finally we return it.

Run your app now (CMD+R)!



Now that we’ve got the basic email user interface completed, let’s add some user interaction. A popular feature of Gmail is the ability to star an email.

Let’s add a star icon to each of the email rows. Open up your EmailTableViewCell.xib file. Search for a button in the Object Library (CMD + Shift + L), then select the button. Drag it onto your cell view to the top right hand corner.


Now open up your attribute inspector and set the buttons image to a star border. Download the star images here.


I’ll also add some constraints to this button in order to lock it into place and set its size.



Run your app (CMD + R) and it should look like this:


User Interaction With Button

We have star button, but it doesn’t do anything. Let’s make this button work!

Open up your EmailTableViewCell.swift and your corresponding .xib in your assistant editor (hold option and click on it). Create an IBOutlet for your button by holding ctrl and click-dragging it into your Swift file.


@IBOutlet weak var starButton: UIButton!

Create a IBAction by clicking on your button in your xib and opening up its Connections Inspector (CMD + Option + 6). Drag Touch Up Inside into your code.


@IBAction func starButtonPressed(_ sender: UIButton) {}

Let’s add some logic to change this button image.

@IBAction func starButtonPressed(_ sender: UIButton) {
    guard let emailModel = emailModel else { return }
    emailModel.starred = !emailModel.starred
    let starImage = emailModel.starred ? UIImage(named: "star-yellow") : UIImage(named: "star-border")
    starButton.setImage(starImage, for: .normal)

The above function does the following:

  1. Check emailModel isn’t nil with a guard statement.
  2. Toggle the starred boolean variable
  3. Get the correct image based on the new boolean value. Yellow star is starred is true and a border star is starred is false.
  4. Set the image to the button

Run your app now (CMD + R):


Clicking the star buttons will work!

One more thing we need to do is update our didSet in our emailModel property to include the button.

var emailModel: EmailModel? {
    didSet {
        guard let emailModel = emailModel else { return }
        senderProfileImage.image = emailModel.senderProfileImage
        sender.text = emailModel.sender
        subjectLine.text = emailModel.subjectLine
        emailExcerpt.text = emailModel.emailExcerpt
        let starImage = emailModel.starred ? UIImage(named: "star-yellow") : UIImage(named: "star-border")
        starButton.setImage(starImage, for: .normal)

So now if an email has been starred before, the star button will be set to the correct image.

Sliding To Archive

Sliding to archive or delete is one of the most used features of the Gmail app. In iOS 11, Apple introduced a couple of methods to make this a lot easier. Open up your MainViewController.swift and go to your extension.

In your extension add a trailingSwipeActions function like so:

    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (action, view, handler) in
            self.emails.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        deleteAction.backgroundColor = UIColor(red:0.24, green:0.49, blue:0.25, alpha:1.0)
        deleteAction.image = UIImage(named: "archive")
        let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
        configuration.performsFirstActionWithFullSwipe = true
        return configuration

You can download the archive icon image here.

This won’t produce the exact same animation as the Gmail app, but it’s a good starting point. Run your application and you should see this.


To be continued

That’s it for now, but stay tuned for the next article in this tutorial.

If you liked this post and want to learn more, check out The Complete iOS Developer Bootcamp. Speed up your learning curve - hundreds of students have already joined. Thanks for reading!

Eddy Chung

I teach iOS development on

Similar Posts