As Nathan wrote last week, we are currently focused on releasing our first Web product. That means we’re spending nearly all of our development time making sure our Web app is fast, reliable, and beautiful.
However, we’re still devoting time to keeping our native iOS app moving forward because we see it as a critical pillar of our mission to make it easy for everyone to collect, organize, and share the files, links, and services they use to get their work done everyday. We spend more of our time on our mobile devices than ever before, and the
We’re hiring an Apple Engineer. If you are an experienced iOS or macOS engineer who would love to help us make sharing easier for everyone, please reach out!
In order to support our goal of building the fastest, easiest, and most secure way to share on iOS (and eventually macOS) we’ve invested a lot of energy in a few key technologies:
- Modular architecture
Betting on WebAssembly
Just as we did for the Web, one of our biggest bets for iOS is on WebAssembly. Our end-to-end encryption code is compiled to WebAssembly and shared between our iOS and Web clients because we want to ensure 100% fidelity. Additionally, in the future, we anticipate sharing more code via WebAssembly in order to aggressively release features across clients without sacrificing reliability.
WebAssembly on iOS is currently enabled by a library we wrote and released as open source: WasmInterpreter, which itself relies on Wasm3. These libraries allow us to ship compiled WebAssembly binaries with our app. Although there is a performance penalty executing WebAssembly instead of natively-compiled binaries, the ability to run exactly the same compiled code in our iOS, Web, and server apps more than makes up for it. And, of course, we are constantly looking for ways to reduce the performance gap between interpreted WebAssembly and natively-compiled code.
If you want to read more about how we designed
WasmInterpreter, check out the series of articles I wrote on the subject.
Betting on SQLite
We use SQLite to power our entire app. We use it for much more than its pure storage capabilities. It powers the app’s unidirectional data flow. Everything in our app—including views, the sync engine, our thumbnail generators, etc.—subscribes to the database via SQL queries. They populate themselves according to the stream of entities returned from the database. Anytime a change needs to be made to an entity, the various components of our app send a message to our database controller, which converts the message into a SQL
DELETE statement and commits the change to the database. In response, the subscribers to the database will receive a new stream of entities they can use to repopulate themselves.
The benefits of this architecture are multifold. First, it’s fast because SQLite is fast, especially when running on iOS devices’ speedy SSD drives. Second, it’s reliable because SQLite is reliable and very easy to unit test. Third, it’s easy to debug because SQLite returns clear error messages and the application state lives in a single database file that is easy to inspect.
This clean architecture is powered by a relatively small, imaginatively-named wrapper around SQLite we wrote and released called SQLite.
Betting on Combine
Combine is often used as the glue between different components of our app. When components of our app subscribe to the database, they use Combine. When our sync engine connects to our service via WebSocket, the servers’ messages and replies are published via Combine. When users import files or links into Shareup, they are processed via Combine-powered publishing pipelines. We have replaced most delegates with Combine publishers because they are easier to test.
Although Combine is plenty powerful by itself, we found ourselves needing to augment its capabilities. We put those changes into an open source project called Combine Extensions. It includes helpful publishers like EnumeratedPublisher, RetryIfPublisher, and ThrottleWhilePublisher. It also includes a slew of test helpers that make it easy to unit test Combine publishers.
Betting on Modular Architecture
One of the lesser-known advantages of Apple’s programming language, Swift, is the Swift Package Manager, which makes it easy to write small, focused Swift packages. Inspired by the work Point-Free did on isowords, we broke our application up into many modules, each focused on solving a single problem. We have a module devoted to our app’s shared models. We have another one that handles generating thumbnails for all of the file types we support. Yet another one includes all of the views used by our iOS app. In all, we have about 36 modules that are composed together to build our iOS app. The Xcode Project for Shareup includes only two source files:
SceneDelegate.swift. Everything else is in Swift packages.
The advantage of this organizational structure is the different parts of the app are isolated and testable. Each module can be built and tested on its own. Therefore, when there’s a bug to fix, it’s easy to isolate the part of the app where the bug is located and iterate quickly until the bug is fixed. Build times are also improved because it’s easy for Xcode to understand what has or has not been changed since the last build. Unchanged modules don’t need to be rebuilt, which speeds up incremental builds. Additionally, modularizing a codebase forces one to explicitly declare dependencies and think carefully about what sort of public interface each module should expose.
If you’re interested in learning about how to use Swift packages to modularize a large codebase, I recommend you watch this video from Point-Free.
Nothing is Forever
One of the principles we hold to dearly at Shareup is flexibility. Although these are the tools we’ve chosen to employ currently, they will not necessarily be the ones we use next year. We are constantly reevaluating our decisions and refactoring our code. However, as of the beginning of 2022, this is where Shareup for iOS stands.
And, we are hiring! If you are excited about helping improve the way people share things and you’re interested in the tech we are using, please reach out about our Senior Apple Engineer job — I want to talk with you. We are a small team. We can move fast, and we are making some exciting tech bets that larger companies/teams probably can’t afford to make.