Tinder on Apple TV - with Swift 3!

Mike Hall

By Shawn Gong, Software Engineer

Success. It can be both a blessing and a curse.

Tinder wasn’t your average startup. The story here is not one of a long and arduous journey to the top. On the contrary, you could say that Tinder was an instant success—a phenomenon—ushering in a new era of human connection across the globe. From the get-go, our developers had to keep up with a rapidly growing user base. High demand and the expectation for new functionality launched the app into an immediate and continuous state of growth.

So what’s the curse? While we often equate growth with progress, I’m reminded of a famous quote: “Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away.” [1] In the case of mobile technology, the struggle between scaling and simplicity can present an engineering challenge of epic proportions. As we built, we built up, every new layer compounding the system’s density. We always knew that at some point in the future, we would need to step back, strip away and streamline. That future is here. This is a story of complexity made simple—and how Tinder on Apple TV came to be.

Swift 3 — The Language of Love

Since its launch, Tinder has added numerous features: Tinder Plus, Tinder Social, Boost, Smart Photos and Super Like, to name a few. As you might imagine, the initial approach to developing the architecture was just get it up and running. Being first to market took precedence over scalability, thus, Version 1 of the app wasn’t designed to support so many feature expansions. As features were added, the logics became more and more complicated, and the code became more intertwined every day. As we scaled, we piled new properties onto a user’s model, even including logics that may have not been necessary. At some point it was obvious that we needed to address scalability at a more fundamental level—it was time to evolve our app with modern technology and cleaner architecture.

I believe a breakthrough comes from multiple opportunities aligning at once. Before I came to Tinder I was working at Facebook, where I had an opportunity to hone my skills as an iOS engineer by working on what is probably the largest iOS app on the planet (by lines of code and number of projects, not the binary size). While at Facebook, Swift was released. I wanted to try it, so I converted 100% of the code in an app I was personally developing to Swift. The results were amazing. I loved that it was explicitly checking for Optionals alongside other modern features the language provided. In fact, my app achieved 99% crash-free sessions. Unfortunately, there was no opportunity at the time to integrate Swift into the Facebook app, so I decided it was time to move on.

Tinder turned out to be the perfect place to leverage the new language in a real-world scenario. The company is still small and exploring new directions, and the team is enthusiastic about innovative ideas that can shape the future of Tinder.

The path forward was clear: Tinder on Apple TV provided the perfect platform to concept-prove some initiatives!

Every Journey Starts with Goals

We decided that the goal of our Apple TV integration should be two-fold: 1) to serve our end users by providing a new use case—the “party” swipe, a fun activity with friends and 2) to create a lightweight, modern app that would serve as an experiment field for new architecture proposals. Thus, we decided to:

  1. Focus on the core swipe experience (without Chat, Settings, etc.)
  2. Design the whole project from the ground up without being constrained by the existing iOS architecture
  3. Experiment on how we can make the app more modular
  4. Use pure Swift and leverage its modern features as much as possible
  5. Build better infra services and libraries which could be directly imported back to iOS

With clear goals and a narrow area of focus, we moved forward quickly. What we achieved in a small amount of time was encouraging.

Much Simpler, More Reliable

Since we decided not to include the Chat or Settings features on Tinder for Apple TV (chatting on a TV would be kind of cumbersome), the data layer becomes amazingly simple. We don’t need to deal with Core Data or any other on-disk storage for chat history, and could just store the Recommendation entities in memory and start from the ground up every time a new app session starts. This is important because our iOS app has suffered from Core Data-related crashes This was due to the fact that we kept adding properties and relationships to the client model, to a point where we couldn’t see clear paths anymore. The clear and solid TV model for Recommendations, which is our core service, will now serve as a reliable guide to model our upcoming evolution of Tinder’s iOS data structure.

With a modular build-up in mind, we created several clean and generic infra components along the way. For example, we built a very generic API Service to deal with Tinder APIs with tokens and meta setups. On top of this API Service, we have several service providers with clear responsibilities to their own roles:

  • Recommendations service to be the sole data source and manipulator of recommendations data
  • User service for user authentication, location and Tinder Plus status check

While this may sound very basic and intuitive, many iOS build-outs are not like that at all—often there are thousands of lines of code because developers keep adding helper methods and there is no clear responsibility for each public method in the Manager. With our new model, each feature service provider will add extensions to the base API Service so that the service doesn’t get polluted with knowledge about every feature. Instead, each feature takes advantage of the base service to build up what’s needed for itself (this is also why we changed the name from API Manager to API Service).

The change goes from this:

To this:

Swift, Type Strong, Yet Beautiful

With Swift’s modern features, we are able to build some neat, yet type-strong interfaces. For instance, each of our Analytics Service event names consists of a type followed by an action, e.g. “App” is an event type indicating that it’s an App-level event, and an action could be “Open”, “Close” etc.

We could define the following:

enum AnalyticsEventType: String {
      case app = "App"
}
enum AnalyticsEventName: String {
      // App
      case open = "Open"
      case close = "Close"
}

Then have a track method with strongly typed params:

func track(_ type: AnalyticsEventType,
           _ name: AnalyticsEventName,
           properties: [String: Any] = [:])

Typically, with ObjC or most other languages, to call such a method would require a long, ugly and hard-to-read call like this:

[analytics trackWithType:AnalyticsEventTypeApp name:AnalyticsEventNameOpen]
Or Java style:
analytics.track(AnalyticsEventType.app, AnalyticsEventName.open)
Thanks to Swift’s type inference, we can now call it (rather simply and beautifully):
analytics.track(.app, .close)
This way, both type safety and conciseness are achieved! We also evaluated our approach to localizations—previously, we would put localized strings in line with code like this:
matchingLabel.text = NSLocalizedString(“Start Swiping”, comment: "Slogan to start swiping in the main menu screen")

Then for each app release, we would have needed to use the Xcode tool to parse the whole project and export an English version of localizable strings—this approach made it very verbose in code and was wasteful for common terms like “OK”, “Cancel” etc. Instead, our new approach is to use localized keys, starting with a beautiful extension to String:

extension String {
      static func localized(_ key: String) -> String {
           return NSLocalizedString(key, comment: "")
     }
}

Adding the corresponding key and comment in the localizable string file for English:

/* Slogan to start swiping in the main menu screen */
"Menu.matchingLabel" = "Start Swiping";

Note that we use Swift enum style to categorize keys, thus common terms like “OK” and “Cancel” could be written like this and used across the app:

/* Common word "OK" */
"Common.ok" = "OK";
/* Common word "Cancel" */
"Common.cancel" = "Cancel";

And again, with Swift’s type inference, using it is enjoyable:

matchingLabel.text = .localized(“menu.matchingLabel”)
let OKAction = UIAlertAction(title: .localized("Common.ok"), style: .default)

We found other Swift features could also be beneficial to development. “Guard” promotes early return for check conditions, and reduces multi-level-nested-bracket-ugliness; project-wide namespacing makes class, struct and enum name prefixing unnecessary, so that the composition and reading of each component becomes much more clear and enjoyable; generics enables us to create all-purpose infra libraries that are concise and work beautifully. We’ve had a blast developing Tinder for Apple TV. As a result, we have already started interloping Swift into our iOS app!

TV is for… Tinder?!

Besides technical experiments, Tinder on Apple TV is also a great farm to test the different ways people interact with apps. In absence of a direct touch screen, the app was controlled by the remote. We made the layout more streamlined to help the navigation. We also incorporated the remote’s unique gestures—for instance, shake the remote and your last swipe will be rewinded). Additionally, the TV’s larger screen adds a communal element to swiping. When we envision Tinder on AppleTV, we imagine our most popular use case is when people get together at home and swipe for each other. We believe that frequently changing users might be an interesting feature for this use case, so we’re looking into multi-peer connectivity technologies to advance the experience in the near future!

Swipe Right or Swipe Left? You Decide

Tinder on Apple TV is now in the App Store. Try it out, let us know what you think and check out our marketing jargon accompanying our dynamite videos here! We are bringing the knowledge, experience, and libraries we gained through our journey back into our Tinder iOS experience. There are great refactoring efforts going on, and we will deliver a faster, more reliable and incredibly Swifty iOS update in the coming months. If you’re excited about our evolution and want to be a part of it, then get in touch with us—we’re always hiring!

  1. Antoine de Saint-Exupery, Terre des Hommes (1939)