Recently I've spent some time thinking over my current iOS development approaches. I was trying to figure out how to make it more efficient.
My current goal is to develop apps faster, so that it would cost less for the end client.
At the same time I want to preserve extendable app architecture. For sake of comfortable and clean development process with ability to handle unexpected turns of clients' imagination.
In other words, I'm trying to find an optimal solution for a trade-off between speed and flexibility, with fixed high quality.
Sounds like an impossible problem.
My Good Old Proven Stack
Long story short it could be called simply VIPER + Service Oriented Architecture.
I Got Rid of VIPER
Though VIPER receives a lot of hate these days, I still consider it to be a pretty good thing. A good way to separate the responsibilities of screens in a clean and consistent way.
All the cons related to tons of boilerplate code I've managed to solve with code and file generation. So VIPER itself doesn't slow me down significantly.
Except for special cases, when I have complex UI, composed of several other screens, where each screen is also a VIPER module.
Especially, when there are too many ways of interactions between those modules, especially including layout-related interactions. Those cases bring a real headache, mainly created and worsened by VIPER.
Sometimes it's also annoying when just a tiny change in screen logic leads to changes in several classes and protocols.
I Try to Avoid Core Data as Long as Possible
I used to love Core Data as a single solution for several things:
- Single source of truth for entities
- Observable models state changes
- Object graph in-memory management
- Persistence that I received for free out of the box
It allowed me to build the whole app in a cool data-driven manner:
- App UI relies entirely on storage,
- All side effects results, like network, requests only update the storage when a response received
- Storage changes pass changes to the UI
A kind of handcrafted unidirectional architecture, where the direction is not actually restricted somehow.
Caveats of Core Data
Sorry Marcus Zarra, but Core Data is incredibly expensive.
There is a lot of additional work required to cover out CoreData's stuff with protocols, create DTOs, etc. As I don't want Core Data's managed objects, contexts and other details to spread across the whole app codebase.
Maintenance of Core Data model scheme also is not something I've always dreamed of.
So now Core Data is my main trash bin candidate.
I came to the conclusion that I don't need them anymore.
URLSession already provides a pretty simple and convenient API for writing handy API Clients. Multipart requests, SSL pinning, and file download/upload are also pretty simple and can be done if needed.
Tools For Building UI With UIKit
By the beginning of 2020, I'd stopped using Storyboards and Xibs for new UI and started building UI with code. Mostly with autolayout.
Now I'm moving to SwiftUI if the minimum target OS version allows and I'm going to use UIKit only in cases where there is no SwiftUI equivalent.
Unidirectional App Architecture
I got rid of Core Data but I still need a single source of truth, a clean way to handle observable changes for data-driven UI, and a unidirectional flow of data.
So I decided to move to unidirectional stuff like Flux/Redux/Elm for iOS.
I didn't pick some existing framework for that. Instead, I've researched some examples on GitHub for inspiration and worked out (and still working on) my own solution that I would be comfortable with.
I've also managed to make it suitable both for SwiftUI and UIKit-crafted UI. So I can use it for any iOS version.
Cache, Persistence, Object management.
If needed I can configure cache on the network layer with URLSession caching capabilities. If the object graph grows too large I can play with some LRU in-mem cache. If persistence is required I can just write the whole state to the file. If the volume grows too large I can add a database just as a side effect.
What I love most in my new approach, is that the core is already quite good and can be extended with anything else on-demand.
I bet that it will allow me to speed up my development at least as soon as I get used to it.
I've started to use development automation tools more heavily. Currently, I use
- SwiftGen for type-safe usage of assets and strings
- SwiftLint with the strict rule s for sake of cleaner code
- Fastlane as a building tools helper
- Github Actions for CI/CD pipeline
Many of those things have always seemed to be overkill for indie devs or small teams but in practice, they turned out to be a cool helper. Yet another quality gate that brings a little more confidence and reduces stress.
How Is It Going?
I've been trying out all those things at once on my side project for some time already and I'm pretty happy with how is it going.
Generally, I've already gotten used to Redux + SwiftUI but I still learn a lot of SwiftUI-specific stuff almost every other day.
I'm not as fast on SwiftUI as on UIKit before. Hopefully will catch up soon.