EarlyBird for iOS

A look into the design process of EarlyBird for iOS

My role at Olympus Labs

I led the design direction of EarlyBird, contributed extensively to the color palette and discussed colors and how certain cultures perceive different colors and how that affects the user experience. I also created refined prototypes to test and validate design decisions. I created prototypes in both Quartz Composer and in Xcode.

The Challenge

EarlyBird from the very beginning was created with the intention to make our lives easier, to make our morning better in our increasingly busy lives. The goal of EarlyBird was to deliver a healthy breakfast to large cities like San Francisco, New York City and DC. A challenge from the beginning was to make this interaction simple and barrier free and complete transactions in single-digit second counts. In order to achieve this goal we had to look at the entire flow of the app. Our goal also brought into question the effectiveness of using the Apple UIDatePickerView, which lead us to design a custom date picker. Apple Pay was a clear requirement and a strategy that makes sense not only for users but for the company as well. Apple Pay users buy more and more frequently according to testimonials from Stubhub and OpenTable, featured in WWDC 2015. Transition growth for OpenTable using Apple Pay grew 50%, it was a no brainer in integrating Apple Pay into EarlyBird.

Ordering Process

One of the initial goals of EarlyBird was to create a very simple experience, an experience that anybody could understand and could buy breakfast in a very short amount of time. To achieve this we had to look closely at a few things. One being, how to select dates and time. Initially we attempted to use the UIDatePickerView but this introduced a few problems. One of the problems was the limit of the API. In order for the UIDatePickerView to accommodate our needs, we had to write a custom class using UIPickerView. Visually, it lacked character that was used by our app, such as tintColors. In addition, the visual problems and technical issues the UIDatePicker introduced, it was hard to navigate, as proven by internal prototypes. People would take too much time scrolling through the picker view and it wasn’t consistent with the flow of the app. Our solution was to divide days and time into two. Selecting a day will introduce the available times for delivery and the user could tap and move on to the next step, which is verifying the destination and paying.

Initial Proposed Solutions

Early stage, we were still in the experimental stage of the product. We had been experimenting with ideas of a new item every day, and breakfast items that could be customized. We also experimented with the idea of multiple items and we even experimented with the idea of having a hamburger menu as a way of navigation. There has been many studies showing how ineffective the hamburger menu actually is. That idea was quickly abandoned. Initial proposed solutions also involved a tab bar. However, after many discussions, we realized that a tab bar wasn’t necessary. People’s order history isn’t critical and it can be buried in settings and a profile didn’t provide any real value. We quickly remove the tab bar because there wasn’t a clear justification for one.

Concerns and Accessibility Issues

There were a few key concerns me and the team had: accessibility and how our color choice affected people who are color blind was a big concern, specially for me. 8% of Americans, mostly men are color blind. It’s important to design an experience that is accessible by everyone. Another concern and one that affected logistics on the business side had to do with the ability to customize orders. It’s much cheaper to make a single basic item that most people will like than to make a complex order requiring extra time. Getting this wrong would’ve had negative impacts on the entire product. This discovery was really early on in the development process of EarlyBird. A few small concerns that we had as a team had to do with a notification system and how users select their shipping address. Originally, we thought having a notification system served the purpose of informing users in what stage their order is. This approach was flawed for a few reasons but the main one has to do with how we interpret notifications. Notifications are typically used to get users’ attention, to inform them of a new message, a friend request, etc. The status of an order in a notifications view didn’t make sense, the same way we don’t get a push notification when our UPS packages go from distribution-center-to-distribution-center. A big concern of mine, apart from the accessibility issues had to do with how users select their shipping address. The logic behind having a search-bar that served the purpose of an address selector was to focus the users’ attention on the item they were intending in buying. However, it didn’t make much sense. Upon prototyping and testing this, users would routinely forget to select their address, or worse, forget to change their address for the appropriate delivery. Another concern I’d like to mention is the contrast-ratio on some of the text, specially near the bottom with the unselected ingredients. This was quite difficult to read with people who have vision problems but also proved difficult to read when the device was in-direct sunlight.

How our app is seen by people suffering from Pratanopia and Deuteranopia.

The "onions" text fails numerous contrast-ratio guidelines.

Iteration #1

We took our concerns and iterated to a version that took care of our concerns relating to a custom order and the logistical implications. We incorporated shipping address and payment type into our shopping flow to avoid the issues of people forgetting to change their address or forgetting to select one. In addition, we wanted to enable people to place future orders and this introduced its own set of challenges.


Our biggest concern and challenge was transaction speeds. We wanted to enable users to place future orders but Apple’s UIDatePickerView, the UI element shown above was problematic for our use case. We prototyped and tested our app with the picker view and it always slowed users down. They would spend a considerable amount of time browsing future dates. Another concern was how limited the UIControl actually was. It was really hard to communicate important details like holidays and time slots that were booked. We proceeded to design our own custom picker view. I wrote extensively regarding the design process of that picker view: In-depth look into designing a custom date picker view.

Iteration #2

We proceeded to design our own custom picker view to create a faster experience and enable us to communicate important details to our customers. We experimented with a few approaches. One of those approaches involved expanding buttons. The idea was to show at a glance what times during the day the service delivers, including the ASAP option.


There were a lot of concerns with this approach. The first concern had to do with the speed of the interface. It was still really slow to browse through our UI, and there wasn’t an indicator indicating if time-slots were available or full. A user could tap morning and there’s a high probability that they’re all booked. This creates a frustrating experience and we witnessed this through our prototypes. Another concern we had and one that we witnessed immediately was the asap button. It did not provide any context indicating what asap means at that specific time. Asap can mean many different things when applying different variables. Our last concern had to do with accessibility. The contrast-ratio failed various guidelines, resulting in a bad reading experience for the visually impaired. The colors we selected for our expanding buttons also resulted in a bad experience for the color blind. They weren’t very visually different and that was a problem in differentiating them apart.

Iteration #3

We took our concerns and iterated. We move past the old approach of expandable buttons into a more accessible approach and one that let’s us communicate important details, such as time-slots that are booked and also holidays. We also wanted a faster way to select future dates. To achieve this, we show the week and by tapping the day, we reveal all of the available times. This decreases scrolling through a ui element or going back and forth between expanding buttons. Upon prototyping this user-interface, we learned that it solved the problems we had with the previous approach. It was a lot faster to browse between days and times and communicating critical information to customers proved much easier.

Iteration #4

We were much happier with this implementation for a picker view but we still had some concerns, mainly having to do with discoverability. Originally, we hid the element under the future table view cell. Upon tapping future, the cell would expand and reveal the date picker. This caused discoverability problems. It wasn’t very clear what was there, as witnessed by people using prototypes. There was a clue that something was there but it wasn’t very clear.

Version 1.0

Our ultimate implementation was a very similar one to the previous iteration. Instead of hiding our date picker, we reveal the available days. Upon tapping on a day, we reveal the times and the user can switch between days to see the times. This approach is ultimately better than previous iterations. It’s much faster to browse for our users, it’s much easier for us to communicate critical information: In-depth look into designing a custom date picker view You can see a prototype of version 1.0 below.

Design Challenges - Handling Declined Credit Cards

Every new project brings a new set of challenges and EarlyBird was no exception. The purpose of EarlyBird was to simplify life, make mornings better and more convenient. It was important to look at every detail and make sure we’re delivering to our users. This logic doesn’t just apply when credit cards are approved and things function as intended, but more importantly, how to handle declined credit cards. Credit cards fail all the time and it happens to all of us. It’s also completely out of our control and we’re usually unaware of it. I could have presented our users a generic alert view saying their payment failed and ask them to try again. This proposition has a lot of problems: 1. It doesn’t actually solve the problem the customer is having and it also introduces a barrier between people and what they’re trying to buy. Cards are typically declined for three reasons: expired cards, fraudulent concerns or late payments. Unless the bank forgot to issue a new card before it expired, the customer probably has a flag on their account regarding fraudulent activity. It’s very important to communicate this information appropriately to customers because failing to do so creates confusion. The user could have used their card the night before to pay for dinner, but have it fail the next morning in the app. People don’t know how banks treat their card activity or what payment type has a higher rate of fraud. Paying at a restaurant has a lower fraudulent rate because it’s a “Card Present” payment, in comparison to a “Card not Present” like online purchases. “Card not Present” transactions are typically declined first before the card stops working in physical locations as well. The solution to this problem was not only to design an interface that didn’t create confusion but more importantly created a path way to a solution. We also wanted to bridge the gap that exist between people and their transaction. If a user has another card on file or Apple Pay, the interface will adapt and suggest the easiest path to a completed transaction. A key part of the interface is to create a path way to a solution. The most likely solution is for the bank to clear any concerns with customers. Our interface presents users the phone number to their bank. The way we designed and implemented this was to understand what the numbers in a credit card stand for. The first six numbers to any credit card is known as the Issuer Identification Number. The first number to any credit card points to the card type. For Amex cards, that number is three, Visa is number four, MasterCard is five and Discovery is six. This logic applies to every payment card because it’s issued by the ISO/IEC 7812 standard. The easiest cards to identify are Amex and Discover because they’re both the bank and the network. Visa and MasterCard introduce a set of challenges because they’re only networks and are used for a lot of different purposes, including pre-paid cards, debit and credit. Companies like Bindb specialize in fraud prevention and license databases to match cards and identify them. By using their database we can match cards to the issuing bank and retrieve the phone number to customer service. Introducing users to the likely solution is very useful and it removes confusion. If people have 10 or 15 minutes, they might call the bank and get a potential fraudulent flag removed from their account by answering a few simple questions. If the user decides they want to fix it later and want to continue with their transaction, they can pick another card on file and finish their purchase. This implementation solves the two biggest problems in standard UI to communicate this problem: instead of creating confusion, it points people to the solution and it also bridges the barrier between people and what they’re trying to buy.

Initial Proposed Solutions

Initially, we approached this problem in a really standard way and we quickly learned just how problematic that approach was. Credit cards get declined all the time and certain apps handle this behavior differently. Most apps present an alert view like the one shown below. Some apps, like the App Store presents the payment view asking users to re-enter their credit card number. We took a very standard approach and that approach is really problematic, as we learned. There’s a lot of friction and frustration when a card gets declined and alert views only make it worse.


We had a lot of concerns with this approach. The two biggest concerns we had was how it affected users and how it affected the business. When a card gets declined, users might not know exactly what happen. Most error messages aren’t very detailed, thus, leading to confusion. When users are confused, they tend to abandon what they’re doing. This approach also hurts the business. A business can’t complete the sale and the user can’t buy what they want to buy. We really wanted to solve this problem.

Iteration #1

Our first iteration was a slightly different approach to that of the standard way of handling this problem. The buy button changed to a red button, indicating an error and proceeding to inform the user of what happen and what they should do next.


We had a lot of concerns about this implementation. The first concern was that it really wasn’t any better than the previous approach, it was just prettier. It still failed to solved the problem. We believed it was much clearer to users that they can select a different card but that wasn’t the case. Users were quite confused and in our tests, users would force quit the app and relaunch to change the color of the button. The other issue was the color. Red means different things to different people, specially those of Asian culture. In Asia, red is associated with prosperity and good luck, not exactly how we perceive it in the western world. Another issue was how poorly it adapted to other languages, not just right-to-left languages but German for example. There wasn’t enough space to communicate critical information and it felt cluttered. There was also issues with legibility, both in terms of size but also color. Because the UI element was quite small, text had to be reduced (which creates issues if your app is using Dynamic Text) but the light red with a red background was hard to read. It wasn’t just shown to me in testing, the contrast-ratio failed multiple guidelines.

The user-interface element fails numerous contrast-ratio guidelines.

Iteration #2

I spent a significant amount of time exploring more viable options. It was really important to enable people to select other cards when one of their cards was declined. It was also important to have an interface that adapts to many different variables, such as different languages, multiple credit cards and is also much more accessible. A modal view made the most sense. A modal view allowed me the space to display multiple credit cards, the white background, against red and black returned a great contrast-ratio, making legibility issues a thing of the past. This was a great step forward from previous implementations.


At this stage, we were quite happy with the results. However, we still had some concerns. Our main concern was that we still didn’t have a path-way to a solution. We wanted people to be able to call up their bank right from the app without having to take out their wallet. We could have asked users for their bank’s phone number when they add their card, but it seems hugely unnecessary and we didn’t want implement a mediocre solution.

Iteration #3

We iterated with the idea of creating a path-way to a solution. The path-way to a solution is not a very exciting one, it’s simply the phone number to their bank. To get this number, we had to do a bit of research. After some research, I discovered the ISO. The ISO is this large organization in Switzerland that standardizes things like currency codes, power plugs, etc. They also standardize how we identify credit cards. The specific standard is the ISO 7812. I wrote a much larger piece on this: In-depth look into handling declined payments

Version 1.0

Ultimately, version 1.0 was a much better approach to the way we typically handle declined credit cards. Credit cards do get declined and they get declined more often than we’d like. If and when they do get declined, we wanted to provide a better experience, an experience that isn’t confusing and an experience that let’s people buy what they want to buy even when their cards get declined. However, I wish I could go back and work on this a bit more. When banks purge credit cards for fraudulent use, they typically issue new numbers or change the expiration date. There’s no easy way to change that within this interface.

Order Status

Order status was important but we didn’t want to add additional complexity to our app. We didn’t want to bury it in settings because it doesn’t belong there, it also doesn’t deserve a tab bar item or a button anywhere in the UI. The order status UI is only visible when needed and it disappears when it’s no longer needed. Order status is an indicator that is indicating something at the current moment. Much like our Apple Watch and its indicator or notification badges, they indicate something and are only visible when they’re communicating its purpose to the user and disappear when the task is complete.

User Feedback

User feedback was critical for us. We wanted to make sure each and every experience was great. Practically, it’s important to know that things do go wrong when people are involved. There was a few challenges in designing a UI that would collect this type of information. 1. It had to be fast and seamless, we don’t want to waste people’s time asking for this information because it creates a barrier between them buying again or leaving. The majority of the time, orders go well and people will report a normal experience. Upon tapping the “it was awesome” button, we quickly dismiss the view and let people on their way. Because of the nature of our service and the logistical complications, it was important to know that in some cases, things will go wrong and we want to hear about them. If a user taps “there was a problem” we present a view asking for basic information and a comment section. The logic behind presenting an additional view is to collect more information and create a path towards a solution. Users who are likely to return to the app after having a negative experience are likely to leave additional details. Internally, we experimented with the idea of analyzing data of the experience when people report a problem. In our prototypes, we would dismiss the view after tapping “there was a problem” and inform the user that they would hear back within 24 hours. This has a big issue. It lacks a fundamental human touch. We could approximate the problem based on times and potential delays in traffic or in the kitchen but the way the user interface handled this interaction lacked empathy, it simply dismissed the view and it failed to gather human words. I also don’t think this works regardless of the user interface, this interaction isn’t normal. When something goes wrong at a restaurant, managers talk with the customer and engage in conversation and come down to a solution. Our idea failed at all of those and we quickly realized it was a bad experience. Instead, we present a view asking people for their words, for them to share the experience they had, the way they would communicate in the real world.

Date Picker

When designing the feature for future orders, it was important to question and very closely critique Apple’s UIDatePickerView for our use case. It’s not a bad UI element, its functionality serves the OS incredibly well. For our use, however it failed in a few key aspects. The UIDatePickerView API is quite limiting, which directs engineering resources to use the UIPickerView and write a custom class to accommodate our needs. Upon building a real prototype in Xcode, the team quickly realized that it’s quite time consuming for our needs. The user needs to scroll and select the date and do the same for the time. This takes too much time and we had to design out own custom date picker. What we did is show available dates and times by simply tapping. Upon tapping a date it will slide down the second UI element displaying available times. We provide feedback in the UI to inform users that their date and time has been selected by underlining the date and time cell.

Sign Up Flow and Progress

Our sign up flow was a hard problem to solve. The reasoning behind this complexity has to do with the availability of our app. Our app didn’t service every area and we didn’t want to lose contact with potential customers. Speed was also an issue, people spent too much time adding information before reaching the order view.

Why This Implementation Failed

After prototyping this potential solution, we learned a few things. First, we asked for shipping information but we didn't provide context to communicate why we needed it. People were hesitant to divuluge this information and users would stop the sign up process. The other contirbuting factor to the failure of this implementation is the speed of the sign up process. People spent far too much time tryign to sign up.

Potential Solutions

After understanding the downfalls of our previous implementaiton, I started sketching potential solutions. To reduce the issue of speed, Facebook Connect made sense. People can tap Facebook Connect and we take care of the rest. In the sketch, I also removed text fields prompting users to enter their address. There isn't a great way to reduce sign up speed and also provide sufficient context. In the sketch and in some of the mocks/prototypes, I experimented with the idea of using the Address Book API. This idea ultimately failed because there wasn't enough data to justify its implementaiton. In the team, I had my personal contact populated, but the rest of the team didn't even know this feature existed

Iteration and Concerns

Affer sketching different possible solutions, I arrived to this implementation. Sign up speeds were much faster, using the Address Book API, iOS let us import their address book and complete their account, the same idea applies to us using Facebook Connect. However, we still had some concerns. First, if the user it not in an available area, a mechanism to inform them when the service reaches their area does not exist. Using the Address Book API was a great idea in theory, we could use people's address book to complete the sign up process but because we didn't have sufficient usage data, it was hard to implement this in the shipping version of the app. If the user doesn't have any info on their personal contact, the user has to sign up the traditional way and it's frusturating because their intention was not fulfilled.

Fixes and Final UI

After many iterations, we arrived with what we believe is an implementation that is fast to onboard users and provides a mechanism for users who are not within coverage. When the user reaches the last page of the on-boarding experience, they area asked to grant the app location service privillages. If the user accepts, the app will get their zip-code and see if they're within an area that the app serves. If they deny the app location privillages, the text asking for permission will change to a text field where they can manually imput their zip-code.

If Service Is Not Available

It was really important from the very beggining to consider that most of our users will not be in an available area. In addition, our users might live witih coverage but not during the day. They could live in SoMA, where the app does service users, but they might work in Cupertino where the app doesn't serve. If a user tries to make an account at work, for example, the app will provide a mechanism to enter their email or provide a different zipcode, incase they're at work.


In settings we wanted to give the option for people to edit and see recent orders or edit credit card, shipping address, email, phone number. We decided to follow a very traditional UI for this function. The reason behind designing settings in a very conventional way was to make it familiar to people.

In-depth posts and look into the design process

I wrote three longer posts regarding the handling of credit cards and how it was required to create our own custom date picker. In these posts, I dive in and discuss the problems, constraints, solution and show how the interface evolved.

Handling Declined Credit Cards

I go in-depth to discuss how I designed the interface, the constraints and the challenges in designing this experience. I discuss learning about standard issuing bodies and how I used the standard to my advantage to retrieve bank phone numbers.

Designing a Custom Date and Time Picker View

I discuss in-depth about the process of designing a custom picker view. Why Apple's standard UIPickerView proved problematic for our use case and I show the evolution of the picker view.

In-depth look into picking our color palette

I discuss in-depth about the process in selecitng our color palette, how each color is seen by different cultures, including our own and how each color affects users wtih accesability.