Meteor and iOS: Implementing a Native iOS App With a Meteor Backend

Nicholas Solter
Posts from Emmerge
Published in
7 min readJan 20, 2016

At first glance, building a native iOS app to run against a Meteor backend might seem foolhardy. The whole point of Meteor is to ease development of cleanly reactive web apps based on a tightly-coupled client and server using not only the same language (Javascript), but for the most part running the exact same code.

However, I’ve spent the past eight months doing exactly that: designing and implementing a native iOS app that works with a Meteor backend. I haven’t seen a lot of discussion about this combination yet, so I’ll share what I’ve learned so far in the hopes that it’s helpful to others trying to do something similar.

First, What is Meteor?

Meteor is an open-source Node.js based framework for building web apps. Its most obvious benefit is the ease of building fully reactive web apps. That is, web apps that update quickly and cleanly when data changes (either driven by the user or pushed from the server), without the user needing to manually refresh or wait.

Why a Non-Meteor Based Native App?

Meteor provides tools for building Cordova-based native iOS and Android apps. So the first and most important question is: Why would one invest significant time into building an independent native app for iOS? The decision for us at Emmerge came down to two points:

  1. Performance. As an email app, we need to compete with the native speed and performance of mail.app and the myriad other options available for iOS. In our prototyping, we quickly discovered that an approach that uses a UIWebView (or WKWebView) to render the content does not give a satisfactory performance for basic iOS functionality, such as infinite scrolling in an email mailbox.
  2. Native look and feel. iPhone users want apps that look and feel similar to their other apps. The easiest way to provide that experience is to use the native controls rather than embedding a web app in a WebView. (And yes, I’m aware of things like React Native, but the default Meteor build for iOS doesn’t use anything like that).

Of course, your mileage and requirements may vary. Your product might be fine as a WebView based app. It’s worth spending some time prototyping before jumping into a decision, as building a fully native app from scratch is a significant time investment.

Emulating the Seven Principles of Meteor

Meteor is designed such that the client and the server work quite closely together, so the main challenge in building a non-Meteor client of any sort (such as an iOS app) is to determine the best ways to emulate a default Meteor client. In analyzing the situation, it’s helpful to examine what Meteor used to call the “Seven Holy Principles of Meteor” (although these principles don’t show up on the Meteor docs page anymore, I still find them to be a useful organizing tool for thinking about Meteor apps).

  1. Data on the wire

Meteor introduced a simple “Distributed Data Protocol” (DDP) that runs over a web socket. The key point at a high level is that only data, not presentation, is sent up from the server to the client. That means that the server doesn’t send rendered HTML to the client; the client does all the rendering. This constraint is great for our purposes — it’s perfect for building a non-Meteor, non-HTML based client. It means that all the data we need on the iOS client is already available via API in pure data form, not hidden amongst HTML and CSS.

More details of how we handled the network mechanics and data fetching/submission to come in a future post.

2. One language

Meteor uses javascript on both the client and server, with much of the code shared between them. You can’t leverage the Javascript in iOS, of course — you obviously have to use Objective-C or Swift on the client.

But beyond the language itself, the fact that code is shared between client and server leads to a muddy distinction between server side and client side code that can present difficulties when adding a non-Meteor client. When the only client is Meteor, it doesn’t really matter if any given piece of code is used on the client or the server — it can be defined in one place and used in both. But this lack of distinction can present issues when you have a non-Meteor client. Any code that runs on the Meteor client side must be replicated on the iOS client even if already exists in the Meteor codebase. This might seem obvious (and is generally true in any project with multiple heterogenous clients). However, the problem is worse with Meteor because of Meteor’s blurred distinction between client and server code. With Meteor, developers don’t need to think much about what code should run on the server vs. the client. Thus, it’s easy to design your Meteor app such that code that could run only on the server runs also on the client, and then must be replicated unnecessarily in non-Meteor clients.

I’ll delve into this topic in more detail in a subsequent post.

3. Database everywhere

Here is where things start to get even more interesting. Meteor introduces an in-memory database called “minimongo” on the client side, in the browser. Code that executes on the client generally reads from and writes to that local database. Meteor keeps the local database and the full mongo instance on the server in sync.

In order to emulate this behavior in iOS, we need a local database on the client. The obvious choice is Core Data, the database framework that’s part of the standard iOS development libraries. Using Core Data presents some challenges, of course. For example, Core Data (by default) uses a relational database, while Meteor uses mongo. Thus, mongo collections must be converted to a relational schema, keeping in mind search queries and the limitations of the native types in Core Data. Also, each document received from the server must be transformed into a row in the relational Core Data database.

Furthermore, there’s no automatic syncing between Core Data in the iOS app and Mongo on the meteor server. Thus, all data change events must be handled manually.

Another important issue to consider is that the iOS client should store its data persistently. There are two main reasons for this: so that we don’t waste potentially precious bandwidth fetching the data every time the user starts the app, and so that users can access the data even when offline. This requirement to keep data between sessions introduces a whole host of synchronization issues that normal Meteor apps, which completely refresh their minimongo client side database every time the web page is reloaded, don’t need to consider.

I’ll have a lot more to say about “database everywhere” in a later post.

4. Latency Compensation

This feature of Meteor is perhaps the single-most effective in providing a snappy, fully reactive user experience. “Latency compensation” is a fancy term for showing the results of user-driven changes immediately in the UI, before the call is made to record the change persistently on the server. Meteor mostly handles this behind the scenes for a standard Meteor web client by syncing the databases, and automatically reverting changes on the client that the server rejects.

In an iOS app, we can emulate this behavior by making the change locally to Core Data, and reverting it if the server call fails. A future post will explore some of the details of this approach.

5. Full stack reactivity

Meteor apps are beautifully reactive — server-side changes can be pushed to the clients across the web socket, and the client immediately updates the UI when its local database is updated.

Emulating this behavior in iOS is actually easier than one might think initially, especially if your UI is table-based. First, once you’re handling DDP, as discussed in the “data on the wire” section, you can subscribe to server-side publications and receive the changes as they occur. Secondly, the UITableView and NSFetchedResultsController classes work really well to create reactive tables in your iOS app.

I’ll delve into the details of this topic when I discuss “Database Everywhere” in a future post.

6. Embrace the ecosystem

Meteor is open source, and there are myriad third-party packages available. While this doesn’t impact iOS development directly, it’s occasionally nice to be able to dig into the Meteor source code to understand exactly what’s going on underneath the surface in order to figure out how to emulate it in the iOS client. For example, in order to implement authentication with Google OAuth from an iOS client, I had to do a bit of research in the Meteor codebase.

7. Simplicity equals productivity

I think this principle is somewhat misleading. Meteor is definitely simple if you stick to the default usage. But there are some complicated things going on behind that scenes that you need to understand in order to build the iOS app. For example, I needed to learn about the Meteor MergeBox in order to write some publications specific to my iOS client.

Summary

In summary, Meteor is a powerful framework for quickly building modern, reactive web applications. At first glance, it does not seem particularly suited to supporting non-Meteor clients such as a native iOS app, but a closer examination of the seven principles of Meteor shows that it’s possible with a bit of creativity.

This post only scratches the surface of the problems and possibilities of building an iOS app on top of a Meteor backend. Stay tuned for more detailed posts addressing the various Meteor principles.

--

--