Summer & Fall 2017
Tulip is a Canadian tech start-up based out of Toronto and Kitchener. Created in 2013, the company develops mobile applications for Apple's iOS platform focused on empowering store associates. Tulip's mobile app typically contains features such as a Product Catalog (where an associate can browse for items that a customer in the store might like), and Clienteling (a technique used by retail associates to establish relationships with key customers based on data about their preferences, behaviours and purchases).
Some of Tulip's current clients include Michael Kors, Saks Fifth Avenue, Kate Spade, and Toys"R"Us.
Recently, Tulip received from Kleiner Perkins which kicked off rapid growth within the company. Since I started at Tulip in May 2017, the number of employees at Tulip has doubled.
ABOUT THE JOB
For both work terms at Tulip I was an iOS Developer. For the first two weeks I fixed bugs within Tulip's core framework to become familiar with the code base. Then, I was assigned to the Indigo project for the duration of my terms; however, the project did depend on aspects of the core Tulip framework which still required me to fix bugs as I found them.
The focus of my job, the Indigo project, is discussed in the section below.
Over the course of both co-op terms, my main project included creating a mobile point of sale app for Indigo; Canada's largest book, gift and specialty toy retailer.
Indigo associates can walk around the store with an iPod Touch device coupled with an integrated Verifone e355 payment terminal that enables customers to checkout without having to go wait in line at the counter. The payment terminal has a built-in barcode scanner that allows associates to scan items to be added to an electronic "cart". Then, the customer can pay for the order using a credit or debit card, such as Visa, MasterCard, or Interac. After the payments are approved, the associate can print or email normal receipts and/or gift receipts. The app also has the ability to create new loyalty members.
Historically, Tulip has had one core product with many different features. However, there is a roadmap to split Tulip features into separate products. Indigo has engaged Tulip with an interest in creating an independent Checkout app. Creating this app would require the development of a new framework that would be used as the core of all future Tulip Checkout apps.
Both the framework and app were created using the Swift programming language; a language that I had taught myself and have used to make my own iOS apps.
When I joined Tulip in May 2017, the Indigo project had just started. My first task was to start building the user interface for the framework (some of the interfaces are shown below). A Tulip designer created initial mockups of the new interface. I had to replicate the mockups as faithfully as possible. Though implementing the UI was fairly straight-forward, coding the logic around the UI functions proved to be challenging because the core Checkout framework had to be generic enough for any future client to use. For example, Indigo has a service that determines the best promotions to apply to the customers order. In the first screenshot below, when the associate taps the "Checkout $65.34" button, the core framework would transition to the third screenshot below to begin the payment process. In the Indigo app, however, tapping the checkout button had to submit the cart to Indigo's promotion service, then a second tap would transition to a payment screen. The solution to this problem was to "inject" code related specifically to Indigo into the core framework. This is called Dependency Injection.
Essentially, dependencies are passed into an object from an external source. In the context of the Indigo app, the app is the external source. The core Tulip framework provides a default implementation. If Indigo needs the behaviour to be something else, we pass the dependencies (code) into the framework from the app.
The app also required the ability to print regular and gift receipts. Indigo provided Tulip with several sample receipts which I had to replicate as closely as possible.
The manufacturer of the mobile printer had a Software Developer Kit (SDK) to allow the app and printer communicate with each other. However, as I implemented the SDK I began discovering edge cases that the SDK couldn't support. For example, if two iPods tried to print to the same printer at the same time, the SDK would get confused and get stuck in a "could not connect to the printer state" until the app was restarted.
I took it upon my self to contact the manufacturer to see if they had any suggestions in order to resolve these issues.
Since Bixolon is a Korean company, and our technical contact was in California, there would often be a 24 hour turnaround to get an answer. Due to language barriers some of the issues could not be resolved over the phone or though Google Hangouts call, so Bixolon flew out one of their engineers so I could work with them directly. After a day and half of working together, all of the issues were resolved.
The payment terminal the Indigo app uses is a Verifone e355 that is controlled by a Moneris SDK. Moneris is responsible for actually taking payments and charging customers while Verifone just manufactures the device.
My manager worked on integrating the SDK with the device while I was responsible for calling his implementation where-ever the app needed to communicate with the payment terminal. For example, I had to call his implementation when it was time to prompt the customer for a payment.
Also, going back to the difficulties of implementing a generic user interface workflow in the core framework, the Indigo app does not pre-authorize payments. Instead, an approved payment actually means that money has been taken from the customer. Also, at the end of a transaction Indigo requires the app to send all data regarding the transaction to one of their API's.
Regarding the API's, I had to consider a situation where the transaction could not be submitted (due to a loss of internet connection, for example). The solution was to implement a custom Indigo UI workflow that would force the associate to refund all payments before starting a new transaction. At this point I had become comfortable using Dependency Injection and the VIPER architecture. Even though implementing this custom workflow did require some refactoring of the core framework, overall it was much easier to implement.
My most significant accomplishment took place in August 2017 when my manager was going on parental leave and my project lead was going on vacation for two weeks. I was the only person at Tulip that knew how the Indigo app worked in great technical detail. During this time I was responsible for communicating with Indigo about all project details, continuing development, testing, and delivering two builds of the app each week. Was it stressful? Yes. But I was forced to manage my time and stay organized which are definitely valuable skills worth practicing.
After six months of development, Indigo launched the app in 10 pilot stores across Canada including Toronto Eaton Centre and Sherway Gardens. The app has been well received from associates in the store and the next phase of the project will begin in 2018 with potential for the app to be rolled out to all Indigo Canda-wide stores in the coming months. If you want to see this app in action just go to one of the pilot stores and check it out for yourself!
Phase 2 is currently underway while Indigo continues to use the app in their 10 pilot stores. The 2017 Christmas rush led to fantastic results for the mobile app. Their most popular stores often see very long lines during the Christmas season, however, now that associates were equipped with the Tulip app, customers would often checkout in under 2 minutes. The Indigo store at Toronto Eaton Centre was able to use the app so efficiently that they grossed their highest sales ever for a financial year. Shorter lines equals happy customers and more sales!
Associates have processed millions of dollars in sales through the app and according to Indigo's 2017 Q4 results, total sales (all stores combined) increased by approximately 8.2%.
You can read a case study about the Indigo project (written by Tulip) by
BROADEN APP ARCHITECTURE KNOWLEDGE
EVALUATE POSSIBLE SOLUTIONS
At Tulip, I developed the habit of asking a lot of questions when I got stuck on a particular problem. Even though I knew creating the Indigo app would be a team effort, my first personal goal was to limit the number of questions I asked by the end of my first co-op term at Tulip.
I attempted to accomplish this goal by first thinking about the problem, then using the internet if I remained stuck, before asking someone for help.
At the beginning of the Indigo project I had to learn about Dependency Injection. Though I did ask a lot of questions about it, I first attempted to read articles online about the concept. As I became more comfortable with the existing code base and the idea behind Dependency Injection, I began to ask less questions. Many times I realized that if I just talked about the problem out-loud to my-self I would end up figuring out what the issue and resulting solution were. For those reasons I believe I achieved this goal.
My next learning goal was to become familiar with different app architectures that can be used when developing an app.
Prior to working at Tulip I had only developed one real iOS app (you might have heard of GryPhone if you are in Guelph). I developed GryPhone using the MVC (Model-View-Controller or Massive-View-Controller) architecture. As the alternate name suggests, the code was largely unorganized and cluttered within a "massive" source file. Tulip's apps are based on a variant of a different architecture called VIPER. Understanding the concept of VIPER wasn't extremely difficult; however, using it with Dependency Injection was.
Because Tulip's Indigo project used VIPER, I was able to become familiar with this architecture since I used it almost every day.
I can now implement View Models, Interactors, and other components of VIPER with ease. I can also easily override/inject components using Dependency Injection.
My key goal for my first work term at Tulip was to be able to think of many solutions to a problem and then determine which solution is best, rather than implementing the first solution I could think of. Also, even if the solution is obvious I wanted to be able to come up with other solutions just to improve my problem solving and critical thinking skills.
Throughout the Indigo project there were several times where I needed to think of a solution. The best example of this is using Indigo's Loyalty API. This API allows the app to search, create, and modify loyalty members (Plum Rewards). In order to create a new loyalty member the system first needs to check whether the customer already exists. If the customer exists, to modify them the system needs a unique identifier for that customer. As a result, up to 4 API calls could made. The API also had several endpoints to assist in this process so I had to consider all possible paths to determine which path was the most efficient.
However, I still found my self sometimes falling into the trap of "just do it the easy way and figure out a proper fix later" and then forgetting about until a few weeks later. For this reason, I continued to work on this key goal during my second work term at Tulip.
TEACH IT FORWARD
After 4 months of working on the Indigo project using a variant VIPER architecture, a goal I had set for my self was to be able to explain the architecture to someone else who had never seen the Indigo code base. My manager was going on parental leave for two weeks and a new iOS Developer was hired to work on the Indigo app with me making this the perfect opportunity to grow this skill set.
Whenever the new developer had questions regarding how parts of the app worked I was able to thoroughly and accurately answer their questions. By the time my manager had returned from his parental leave, the developer had a solid understanding of the Indigo code base.
In addition, whenever there was phone call or meeting with Indigo I wanted to improve my communication skills with technical and business stakeholders.
As the end of Phase 1 was approaching for the Indigo project, Tulip and Indigo had several phone calls to detail technical limitations and to discuss the possibility of adding some last minute functionality. During these client-facing project calls I successfully explained existing limitations that would prevent the new functionality from working correctly, and I explained how we could go about implementing the new desired functionality.
Indigo understood what I was saying and would work together to come up with a middle ground that was acceptable to everyone.
ALIGN WITH BUSINESS NEEDS
No code is perfect and there is always either a bug or race condition hiding in some sneaky edge case. Then there are the bugs that have obvious fixes and you find yourself asking aloud: "how did this ever work?" or "wow how did I miss that?".
Every week during the project, Indigo provided Tulip with a list of bugs found in their QA testing. It was easier to fix the lower ranked priority tickets first, since they were usually easier and quicker to resolve. However, Indigo wanted the critical bugs fixed first since the highest priority issues were critical to their business success.
After realizing the business implications of focusing on "low hanging fruit", I worked towards fixing the higher priority tickets (or at least attempting to!) before moving down the list.
Though temptation sometimes lead me to fix the quick and minor bugs, by the end of the work term I had become accustomed to fixing critical bugs first, much to Indigo's appreciation!
As a continuation to the problem solving
goal described directly above, this goal involved critically thinking about how a change in one part the app could affect the rest of the app.
By July 2017 the Indigo app and new core Tulip framework had large code bases. As Indigo logged bugs they found during testing I noticed that the fixes I implemented typically had unintentional side effects that would cause another part of the app to break.
For this reason, every time I made a change, I made it a priority to find areas in the app that use the code I changed and test this area thoroughly before delivering the next version of the app to Indigo.
Within a few weeks the number of reopened bugs or new bugs significantly decreased. Near the end of my second work term I also started learning about how to implement test cases in Xcode so that I could begin automating some of the testing that needs to be done.
During my eight months at Tulip, I participated in two Hack Days. A Hack Day (also known as a Hackathon) is when all regular business work stops for the day and everyone at the company focus' on learning new technologies and creating functional software with them. At Tulip, there are two rules:
- Hack Day starts at 8am and ends a 4pm
- At the end of the day, you must present your creation no matter what (even if it doesn't work!)
My first Hack Day project was to learn about iMessage apps and make a simple game that would allow two players to verse each other through iMessage. The game I decided to make was Squares. Given a grid, the goal of the game is to draw as many squares as possible (each square is one point). A player can only draw one side of the square per move; therefore it could take a maximum of 4 moves to draw a square and earn a point. Once the entire grid is filled, who ever has the most points wins.
Before this project, I had never attempted to make an iMessage App nor looked at any of Apple's documentation regarding how to make one. Making UI and implementing the logic behind the actual game was fairly straight forward; however, the core of creating an iMessage App is being able to pass data between the two devices. Implementing this part was not what I expected it to be. I thought all I would need to do is send data in a Dictionary structure (a type in Swift that is based on a hash table). A example of this type of a structure would be:
var dictionary: [String : String] = [:]
dictionary["FirstName"] = "Domenic"
dictionary["LastName"] = "Bianchi"
["FirstName": "Domenic", "LastName": "Bianchi"]
var components = URLComponents()
var queryItems = [URLQueryItem]()
queryItems.append(URLQueryItem(name: "FirstName", value: "Domenic"))
queryItems.append(URLQueryItem(name: "LastName", value: "Bianchi"))
components.queryItems = queryItems
The correct way to pass data is by using a URL. iOS and iMessage apps have the ability to be internally linked to URL's (for example, three screens in an app could have three different URL's). Therefore, by being forced to pass a URL, this enables the option to open a specific screen when the user taps on the message. so I could see why Apple choose to use the URL structure instead of a Dictionary. For the purposes of my app, having to create and parse a URL unfortunately included extra overhead (as shown below) that I would have liked to eliminate.
If I had more time I would have liked to clean up the UI to make it look more user friendly and added the option to have a dynamic board size.
My second Hack Day project involved working with two other iOS Developers to create a macOS utility app that would be used internally at Tulip. At the company, there are several predefined attributes and build settings for each client project that would be tedious to do for each client. Git repositories need to be set up and customizations for the client need to be applied. The goal of this macOS application was that any developer or project lead could use this application to create the base of the client project in a matter of a few clicks.
Just like the iMessage App, none of us have ever attempted to develop a macOS app. The one thing I did know was that even though iOS apps and macOS apps can be coded using the same programming languages (Swift or Objective-C), the SDK's do have differences. For this reason we acknowledged that we wouldn't be able to get everything we wanted to do done within the time limit. Our final product at the end of the day would be able to create a new Xcode project with a Git repository, laying out the framework for the application to grow during the next Hack Day.
For example, iOS has a structure called a Table View. A Table View is a way to display a list of items that can be grouped together or displayed in sections. Looking at the three screenshots above in the Indigo section, these are all Table View's with multiple sections. Whenever you tap on a row in a Table View, a delegate function is called (in simple terms, a delegate function is a function that Apple pre-defines in the SDK. If you class implements that function you can capture the tap and run any code that is required.).
macOS also has a Table View. However, this Table View type does not behave like a iOS Table View. It has different properties and delegate functions. As a result, my team had to learn how to implement Table View's and other structures for the macOS environment.
Tulip had an existing Ruby script that already did some of the functionality that we were implementing in the new application so we decided to use the Ruby script in the application. Much to our surprise, adding a Ruby script to a macOS application was not easy. After a few hours of looking through Stack Overflow and experimenting with our own ideas, we finally were able to get the script to run. By the deadline, we had a working app that could create a Xcode project and git repository.
The eight months I worked at Tulip were beyond anything I expected. Throughout my work terms I learned several valuable skills and was able to take part in several technical and business related meetings. My manager, Terry, taught me many Swift tips and tricks and got me out of some bad habits. The skills he has taught me will definitely be valuable for future jobs and school projects.
Working on the Indigo project was at times challenging but always fun. Being able to collaborate with the stakeholders at Indigo was filled with lots of learning experiences that have helped me grow professionally.
To any co-op students reading this I would definitely recommend you apply to Tulip, even if the position is not for an iOS Developer. The culture at Tulip is awesome and everyone wants you to succeed!
I would like to thank everyone at Tulip for welcoming me and creating awesome memories that I'll never forget. More specifically, I would like to thank the iOS team at Tulip (Terry, Sahil, Adam, Chuks, and Elad) for assisting in the on boarding process, helping me become familiar with Tulip's core frameworks, and helping me understand Objective-C.
A a very big thank-you goes out to Terry Latanville, my manager (and Guelph alumni!). Without him, there is absolutely no way I would have learned what I have learned and been able to assist in the development of the Indigo app. When I first joined Tulip and he introduced me to the concept of Dependency Injection and the VIPER architecture I had no idea what he was talking about. Nevertheless he took the time to guide me through it.
Thank-you Shimaa Hassan, our UI/UX Designer, who not only created the user interface mockups but also valued my thoughts on what could be improved in them.
Thank-you Joe Najduk, our Project Lead, who helped get the project back on the rails when it veered off and helping me communicate with Indigo during meetings and phone calls.
Thank-you Sahil Gupta, an iOS Developer, who helped out and put together the user interfaces related to loyalty members while also helping me take advantage of existing code in other core frameworks to create new loyalty members.
I also would like to acknowledge everyone from Indigo who assisted in debugging efforts and API integration.
Finally, I would like to thank all the faculty involved in the Cooperative Education program at the University of Guelph. Since these were my first two work terms, I had very limited interview and resume/cover letter writing skills. The co-op program helped me prepare my resume and cover letter by not only verbally explaining the components but also offering a peer-reviewer to provide individual feedback. Also, the Introduction to Coop Education course (COOP*1000) provided useful information on how to prepare for interviews, set goals for my work terms, and network.