This document discusses the evolution of iOS app architecture over time from Level 1 to Level 5. A Level 1 app is a simple monolith, while a Level 2 app introduces some improvements like protocols and testing. Level 3 introduces modules but causes slow build times. Level 4 splits apps into API and implementation modules connected by protocols to improve build times. Level 5 introduces a dynamic dependency injection system to allow flexible navigation between modules.
Coefficient of Thermal Expansion and their Importance.pptx
Preparing For Growth: Architecting Giant Apps for Scalability and Build Speed
1. Preparing For Growth 16.04.2021 1
Spotify
Preparing for Growth
Architecting Giant Apps for Scalability and Build Speed
Bruno Rocha - iOS Engineer @ Spotify
3. Preparing For Growth 16.04.2021 3
Spotify
"Level 1" app
A monolith
All Classes All Resources
Features
- Single module
- MVC
- Simple features
- No testing
- Direct navigation
MyAppModule
4. Preparing For Growth 16.04.2021 4
Spotify
"Level 2" app
A larger (but better) monolith
MyAppModule
Feature1 Feature2
Feature3 Feature4
Feature5 Feature6
Feature7 Feature8
The app is now larger, but not
large enough to be a big issue
New Features
- Usage of protocols and composition
- A more testable architecture
- Features are slightly more complex
- Presence of unit testing
- Navigation with injection
6. Preparing For Growth 16.04.2021 6
Spotify
Onboarding
"Level 3" app
A modularized app
Home
FeatureFlags NetworkSDK
New Features
- More modules means less cache
misses
- Compilation of modules individually
- Better structure/division of concerns
in general
9. Preparing For Growth 16.04.2021 9
Spotify
Dynamic Frameworks
.dylibs loading in runtime might cause launch
performance issues
Each additional framework that your app loads adds to the launch time.
Although dyld caches a lot of this work in a launch closure when the user
installs the app, the size of the launch closure and the amount of work done
after loading it still depend on the number and sizes of the libraries loaded.
Linking Static Libraries instead can improve launch time
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
12. Preparing For Growth 16.04.2021 12
Spotify
Direct Dependencies
Directly depending on concrete
implementation modules makes cache
invalidation more common and makes the
app inflexible (harder to add new screens in
the middle of the flow)
Module Module Module Module
Module Module Module Module
Module Module Module Module
Module Module Module Module
17. Preparing For Growth 16.04.2021 17
Spotify
Protocols
FeatureAPI
Classes/Structs
Feature (Concrete)
Protocols
Feature2API
Classes/Structs
Feature2 (Concrete)
Concrete modules shouldn’t
depend on other concrete modules!
API/Impl Module Pair
Incremental build time issues may still exist when making
changes directly in the API modules, but they are now a
much rarer occurrence.
18. Preparing For Growth 16.04.2021 18
Spotify
Protocols
FeatureAPI
Classes/Structs
Feature (Concrete)
Protocols
Feature2API
Classes/Structs
Feature2 (Concrete)
How to navigate
to Feature2?
Dependency Engine
20. Preparing For Growth 16.04.2021 20
Spotify
SPTServiceSystem
Manages a graph of SPTServices, working as a runtime
dependency injector that handles the lifecycle of every
Spotify feature.
Feature
FeatureAPI
Feature
FeatureAPI
Feature
FeatureAPI
Feature
FeatureAPI
Feature
FeatureAPI
Feature
FeatureAPI
MainModule (.app)
🗒 ClassList
21. Preparing For Growth 16.04.2021 21
Spotify
Networking (Concrete)
NetworkService: NetworkAPI
AppDelegateModule (.app)
(SPTServiceSystem)
Hey! I have a service that
implements "NetworkAPI".
👍
HomeModule
Hey! Can you provide me the impl of
"NetworkAPI"?
👍
(Inject "NetworkService")
22. Preparing For Growth 16.04.2021 22
Spotify
Service Scopes
A group of services, allowing fine-grained service access control.
1 2 3 4
LoggedInScope
5 6 7 8 9 10 11 12
13 14 15 16
17 18 19 20
LaunchScope
CarPlayScope
SiriScope
HomeScope
23. Preparing For Growth 16.04.2021 23
Spotify
final class SiriIntentsService: SPTService, SiriIntentsAPI {
}
func load() {
print(”SiriService loaded”)
}
func unload() {
print(”SiriService unloading”)
}
static let serviceIdentifier= SiriIntentsAPI.self
@Dependencyvar connectivity: ConnectivityAPI
@Dependencyvar keychain: KeychainAPI
import ConnectivityFeatureAPI
import KeychainFeatureAPI
33. Preparing For Growth 16.04.2021 33
Spotify
Module Module Module Module
Dynamic Navigation
Not having explicit dependencies mean we can go anywhere from anywhere
Module Module Module Module
Module Module Module Module
UI
34. Preparing For Growth 16.04.2021 34
Spotify
final class ProfilePage: SPTService, ProfilePageAPI {
}
func load() {
navigation.register(”page-profile”) { _ in
return ProfileViewController()
}
}
static let serviceIdentifier= ProfilePageAPI.self
@Dependencyvar navigation: NavigationAPI
import NavigationFeatureAPI