Building a Full Stack application with Swift
Rate this code example
To test this out and learn about iOS development, I decided to build a full-stack application entirely in Swift. I settled on a familiar CRUD app I've created a web version of before, an application that allows the user to manage a list of kittens and information about them.
I chose to build the app using the following components:
I was able to combine all of this into a single code base with a folder structure as follows:
In the rest of this post, I'll discuss some of my takeaways from this experience.
As I mentioned above, I created a shared SwiftPM package for any code I wanted to use both in the frontend and backend of my application. In that package, I defined
Codabletypes modeling the data in my application, for example:
When you use separate code/programming languages to represent data on the frontend versus backend of an application, it's easy for implementations to get out of sync. But in this application, since the same exact model type gets used for the frontend and backend representations of kittens, there can't be any inconsistency.
Since this type conforms to the
Codableprotocol, we also get a single, consistent definition for a kitten's representation in external data formats. The formats used in this application are:
For a concrete example of using a model type throughout the stack, when a user adds a new kitten via the UI, the data flows through the application as follows:
- The iOS app creates a new
Kitteninstance containing the user-provided data
Kitteninstance is serialized to extended JSON via
ExtendedJSONEncoderand sent in a POST request to the backend
- The Vapor backend deserializes a new instance of
Kittenfrom the extended JSON data using
Kittenis passed to the MongoDB driver method
With all these transformations, it can be tricky to ensure that both the frontend and backend remain in sync in terms of how they model, serialize, and deserialize data. Using Swift everywhere and sharing these
Codabledata types allowed me to avoid those problems altogether in this app.
Despite having never built an iOS app before, I found my existing Swift experience made it surprisingly easy to pick up on the concepts I needed to implement the iOS portion of my application. I suspect it's more common that someone would go in the opposite direction, but I think iOS experience would translate well to writing a Swift backend too!
I used several Swift language features such as
in both the iOS and backend code. I was also able to take advantage of Swift's new built-in features for
throughout the stack. I used the
to send HTTP requests from the frontend, and I used Vapor and the MongoDB driver's
asyncAPIs to handle requests on the backend. It was much easier to use a consistent model and syntax for concurrent, asynchronous programming throughout the application than to try to keep straight in my head the concurrency models for two different languages at once.
In general, using the same language really made it feel like I was building a single application rather than two distinct ones, and greatly reduced the amount of context-switching I had to do as I alternated between work on the frontend and backend.
In conclusion, my first foray into iOS development building this full-stack Swift app was a lot of fun and a great learning experience. It strongly demonstrated to me the benefits of using a single language to build an entire application, and using a language you're already familiar with as you venture into programming in a new domain.