How To Make Gmail iOS App in Swift

By Eddy Chung

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:

img1

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).

spotify_50

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

spotify_51

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

User Interface Elements

gmail

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.

gmail_1

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

gmail_2

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.

gmail_3

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.

gmail_25

@IBOutlet weak var tableView: UITableView!

Now create a viewDidLoad function:

override func viewDidLoad() {
    super.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:

gmail_4

Be sure to subclass UITableViewCell and check Also Create Xib.

gmail_5

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:

gmail_6

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).

gmail_7

Next create another label for the email subject line.

gmail_8

Here are my settings for this label:

gmail_9

Lastly create a label for an excerpt of the email:

gmail_10

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

gmail_11

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.

gmail_12

Constraints

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.

gmail_13

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

gmail_14

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:

gmail_15

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

gmail_16

For the image view I added these constraints:

gmail_17

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

gmail_18

Profile Image

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

headshotforgoogle

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:

gmail_19

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.

gmail_20

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.

gmail_21

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.

gmail_23

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.

gmail_24

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)!

gmail_26

Star

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.

gmail_27

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

gmail_28

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

gmail_29

gmail_30

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

gmail_31

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.

gmail_32

@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.

gmail_33

@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):

gmail_34

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)
            handler(true)
        }
        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.

gmail-archive

To be continued

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

If you liked this post, you'll love my free guide: Secrets To iOS Development. Speed up your learning curve - hundreds of students have already downloaded. Thanks for reading!
Profile Picture of Eddy Chung

Eddy Chung

I am a professional iOS developer in Silicon Valley. I teach iOS development on ZeroToAppStore.com. If you'd like to learn more about me click here or you can contact me at: eddy@zerotoappstore.com

Similar Posts