Before writing a single line of code for an iOS app, it’s crucial to design a high-level system and mobile architecture. Here’s why:
You will identify external dependencies
Foresee risks and limitations
It is both easier and faster to modify a diagram than it is to modify code you’ve already implemented
It forces you to clearly define your ideas
This series of posts will guide you step-by-step through designing and implementing a complete system for an iOS app. Using Spotify's iOS app as inspiration, we’ll start with high-level system design and progressively dive into detailed code implementations, building the app piece by piece.
Here’s what this post will cover:
The problem statement: Build a Spotify clone.
App Requirements: Define functional and non-functional requirements.
High-level system design: Identify external dependencies (APIs, databases, third-party services).
The iOS mobile architecture design.
What you’ll learn:
How to derive functional and non-functional requirements from a problem statement.
How to identify and diagram external dependencies like remote APIs, authentication services, and databases.
How to design and diagram the key components of your app’s mobile architecture.
An understanding of how to approach system design for an iOS app.
This is the first post of our in-depth guide covering iOS system design. In our following, exclusive posts for subscribers, we’ll get into the implementation details further, featuring code examples that implement the architecture, integrate APIs, and demonstrate scalable, testable code.
Time to get our hands dirty and dive in!
The Problem Statement: Build a Spotify iOS app clone
The App
Build a clone of the Spotify iOS app solely focused on music. Users should be able to search songs and create playlists. Additionally, they will get music recommendations.
Please note that Spotify is a fully-featured app with media other than just music (ie: video, podcasts, etc.) and a range of additional capabilities have been added since it first launched - we are not going to concern ourselves with any of their more recent functionality, instead we are aiming for an MVP with the core functionality of listening to music and building playlists.
Target Audience
Our target audience is global, so we should be able to support a large scale of music listeners and music data.
Functional Requirements
There should be a home screen where users see their listening history as well as music recommendations. They should also have the ability to search for songs and build playlists. Lastly, and most importantly, users can play music media.
Non-functional requirements
The app should support offline usage, meaning users have a way to listen to music even when they don’t have network connectivity. The app should support large amounts of data, since there are millions of songs available for listening. The app should be responsive and performant.
Wireframes
Now that we know what functionality we are building and what its properties are, in order to get a better picture of what our app will look like and how it will flow, let’s build some wireframes!
I personally enjoy using a diagramming tool called Excalidraw, so the diagrams you will see were created with it.
Below is a rough sketch of our apps core screens I identified, namely: Home Screen, Search Screen, Library Screen, and Music Player Screen.
Now that we have identified the core screens, we will dive a bit deeper into each of the core screens to see if we can add any important details and interactions.
Our app will eventually require authentication for account management, but we did not include a login/sign-up screen above. The reason is that for now we are only concerned with core screens specific to our apps core use case.
Home Screen
The first screen we will explore in further detail is our Home Screen. This screen is one of the most important as it’s where our apps core experience will begin. Here is our initial version of the Home Screen again:
Couple things to note:
The home screen contains users' listening history under the recently played section.
It contains a list of music recommendations.
We will limit how many of our recently played songs we will display, currently my design implies just two most recent songs, though it can be modified.
The music recommendations list will have a larger amount of items, so the user will have to scroll through them.
One important thing to note is that we will have three tabs: Home, Search, and Library. This implies we will need to support navigation between multiple screens and flows.
NOTE: If you aren't yet comfortable with navigation in iOS apps, I highly recommend the following articles, in order:
Our home screen will be interactive, so we need to understand what happens when the user interacts with its' contents.
The first couple of things that come to mind is what happens when users click on recently played items or recommended music items. I’ve decided that when users click on them, they should transition to the music player screen for the media item they click on.
Below is our wireframe for that:
Search Screen
Our next major screen is our Search Screen, where users can search for music. Below you can find it’s design:
Off the top you can see that I decided to include its two states: not searching and actively searching. In terms of interactivity, users can tap on the search bar or click on a recommendation. Below are the updated wireframes depicting what happens with each action:
From this depiction we see three actions and results:
Tapping on the search bar goes to the actively searching state.
Typing in the search box displays results as they become available.
Tapping on a search result navigates to the music player screen.
Tapping on a recommendation from the search screen also navigates to the music player screen.
Library Screen
Our library screen will be a simple list containing the users playlists. There may be several so it will be a scrollable list view. Again, here is our depiction of the screen:
When users tap on a playlist item from the list, users will get navigated to the Playlist Screen. Here they will view the songs inside the selected playlist. Lastly, clicking on a song within the Playlist screen will navigate to the Music Player screen. Below is the wireframe for this:
Music Player Screen
Last but not least is the Music Player Screen. Perhaps the most foundational screen of all, after all, it is a music app we are building. This screen is where users will listen to music, forward/rewind songs, skip or replay songs in a playlist, and add songs to playlists.
I’ve added more details to our initial wireframe describing what each control does and actions:
External Dependencies
Next on our list is identifying any external dependencies, this can include but is not limited to: APIs, third-party services, databases, etc.
Before getting into it, let us review the original requirements:
There should be a home screen where users see their listening history as well as music recommendations. They have the ability to search for songs and build playlists. Lastly, and most importantly, users can play music media.
From these requirements I gathered a few important points:
We will need some way to identify users in order to recommend music to them and store their playlists. For this I decided our app will require users to create an account and sign in. So we need an Authentication Service.
We are likely going to be dealing with large amounts of data (the music), so our app will need to fetch this data from a remote API.
In order to ensure users' music playlists are not lost, we will store them in a remote database.
From these high level observations I was able to come up with the following external dependencies diagram:
The identified items are as follows:
Authentication Service: used to provide account creation and management capabilities.
Playlists API: Used to save and retrieve a user's playlist data.
History API: to keep track of a user’s recently played music.
Music Recommendations API: where our app will get music recommendations for our users.
Search API: to assist our app in searching vast amounts of music.
Media Storage: Where we actually get the music media we play.
For the purposes of this post I will not be diving into the specifics of the entire backend system, we are only concerning ourselves with the APIs visible to our iOS app, so this diagram suffices. More importantly, it helps us in recognizing what services we will need to include in our iOS mobile architecture diagram.
iOS Mobile Architecture
At last, we are ready to explore what our iOS mobile architecture will look like. Here is a recap of what we've done so far:
We reviewed the problem statement and identified the target user as well as both functional and non-functional requirements.
We sketched up some wireframes to get a more concrete idea of our core screens and basic user flows.
We identified external dependencies that our iOS app will depend on in order to function appropriately.
We did all that work before starting on the mobile architecture because it helps to simplify and scope our mobile architecture. If we had started backward we would find ourselves having to go back and add/remove components. Let me demonstrate how following this process in order makes our job of designing a mobile architecture easier.
Below you can find our iOS mobile architecture diagram:
This architecture follows many of the principles in Clean Architecture: separation of concerns, abstractions using protocols, and layers for the view, business logic, and infrastructure.
Starting from the top, going left to right, we have:
View Layer
Views: What the user sees and interacts with.
View Models: Used to abstract our services and prepare data for use by our views.
Coordinator/Router: Used by view models to trigger navigation to other screens.
Business/Services Layer
Auth Service: For account creation/management capabilities.
History Repository: For storing and retrieving history data.
Music Recommendations Repository: For storing and retrieving recommendations.
Playlist Repository: For storing and retrieving playlists data.
Music Search Service: For searching through vast amounts of music.
Media Streaming Service: For streaming music.
Infrastructural Layer
Cache Service: Used by the repositories for caching data locally on the device.
Network Service: Used by the repositories for retrieving data from remote APIs.
Of course this architecture diagram is likely not the final version and may need to be modified some more as we begin getting into implementation details, but it gives us a great starting point to base our code off of.
In practice we rarely get it right the first time, so in the following posts we will truly see the benefit of starting out with a systems diagram that we can use to identify potential issues early on before writing a single line of code.
What’s Next
Now that we've outlined the problem, defined the requirements, and created high-level system and architecture designs, it's time to move from theory to practice. In the next exclusive posts for subscribers, we’ll dive into the implementation details. Here's what you can look forward to:
Building the Architecture:
Translating the system design into code.
Setting up the View, ViewModel, and Coordinator/Router layers.
Implementing reusable and testable components.
Integrating External Dependencies:
Connecting the app to the external APIs, such as the Music Recommendations API, Search API, and Media Streaming Service.
Handling authentication.
Optimizing Performance and Scalability:
Leveraging caching strategies for offline usage.
Testing and Iteration:
Writing unit tests for services and repositories.
This hands-on implementation guide will not only showcase how to write scalable, maintainable code but will also provide you with reusable patterns for future projects. Don’t miss out—subscribe now to access this exclusive content!
Subscribers can find our next posts in this series below:
Comments