At this year's WWDC, Apple caught us, developers, by surprise with the announcement of new frameworks and platforms such as SwiftUI, Combine, iPadOS, Project Catalyst, and many other fantastic updates.
Once more, iOS developers are looking at a fantastic opportunity for getting ahead in the game by mastering new technologies and providing outstanding value in the iOS job market.
However, for many iOS developers, the news denotes massive paradigm shifts they have to go through to stay up to date with the new industry trends.
And just like when Swift was introduced, we noticed many developers being afraid of missing out on this fantastic opportunity.
Of course, as part of the Essential Developer community, we don't want you to be left behind. So, in this article, you’ll learn what’s the ultimate strategy to adopt for:
- preparing your codebase for the upcoming migration to take advantage of all the new platforms and frameworks, such as SwiftUI
- extending your supported platforms by separating your components into platform-agnostic and platform-specific
- thriving through tectonic changes in our industry such as language and paradigm shifts
Although exploring SwiftUI’s capabilities on pet/test projects with no need for long-term maintenance can be fun and educational, adopting the new changes on iOS codebases backing commercial products bears a lot more effort and risks.
For iOS developers that create and treat the UI and other frameworks (especially 3rd-party) as standalone frameworks that can be plugged into the business logic, the migration to new technologies such as SwiftUI will be simple, fast and remarkably low-cost. That’s because there is a low degree of coupling between the business logic components and the UI code of the application. For example, with decoupled logic, developers will only need to replace the current UIKit module with a new SwiftUI implementation (the business logic stays untouched!).
On the other hand, codebases with high coupling between the business logic and the UI will have to be rewritten (or have parts be rewritten) to adopt the new APIs. The cost for migrating to SwiftUI, in this case, will be much higher.
Migrating business logic from a UI framework to another is a risky and slow process, with chances of breaking existing features and business logic since there’s no clear decoupling between the UI and the core logic. Many teams in that situation might rightly decide not to migrate at all.
Additionally, the iOS team will have to shift its focus to rebuilding existing functionality to the new UI paradigm, instead of directing their efforts on extending the app’s functionality and enriching the product. Moreover, by migrating existing functionality, testing the app’s behavior with the new UI components will most likely have to start from scratch.
A better approach to migrating core logic from a UI framework to another is to start extracting and decoupling the business logic from UIKit or any other UI framework.
SwiftUI is iOS13+, and since most companies support at least two previous iOS versions, that gives you enough time (~2 years) to prepare your codebases for a smooth migration.
The first step for preparing to migrate to SwiftUI and the new frameworks and platforms is to establish low coupling between your application layers (a modular approach).
Aim to isolate the UI components from business logic components through iterative refactorings over time. In other words, strive to work in small batches (frequent small commits and merges) guided by a fast and reliable suite of tests.
By isolating the business logic from UI components, you essentially treat the UI like any other framework, a “plug, and play” solution. Then, you can compose all modules in the Main iOS application (Composition Root).
The separation and homogeneity between the modules contribute to your team’s and codebase adaptability towards big paradigm shifts we’ve been experiencing in the past years (the unknown that the future holds!). As a result, the cost for changing even whole layers, such as the UI of your application, becomes substantially smaller.
Adding a watchOS application with a SwiftUI implementation, for example, would be fast and simple as you can reuse the domain logic:
By striving to achieve isolation and uniformity in the codebase modules, you can also witness the following very welcoming side-effects:
- faster development and testing times
- increased happiness and fulfillment within the iOS team
- smoother operations lead to better outcomes and subsequently faster career progression
- faster time (and adaptability) to market
- less confusion and miscommunication within the iOS team, especially new team additions
- more controlled management of state throughout the system
- fewer bugs and defects
- less exposure to risk for new technologies/requirements/experiments as they become isolated and easily replaceable
Another benefit of organizing your code into isolated homogenous modules is the separation of the platform agnostic business logic components and the platform specific ones. Migrating your iOS app to SwiftUI will be just a matter of composing your application differently:
You can even start delivering better SwiftUI experiences to your customers while supporting old iOS versions by having different compositions of your application depending on the iOS version:
Classifying components as platform-agnostic and platform-specific enable us to reuse a significant portion of the codebase for deployment to multiple platforms.
You can represent business logic with raw Swift types (classes, structs, enums), which makes it platform-agnostic. The same business logic components can be used for different deployment platforms depending on the UI you’d like to plug them. In other words, you can end up with the same “brain” of the app (business logic components) but deploy on the iOS, watchOS, tvOS, iPadOS, macOS, Linux, and even the web.
You can even decouple the UI from the domain module when needed (e.g., to reuse UI elements in separate applications with different domain logic).
Xcode 11 makes it extremely easy to extract code into independent platform-agnostic frameworks and packages that can be shared.
The second step for preparing to migrate to SwiftUI and the new frameworks and platforms is to extract components into decoupled frameworks and packages (modules).
Achieving modularity enables you to deal with change much more straightforward, even in rare and unpredictable events where the whole industry must shift and adopt the new standard.
A modular design drives down the cost and time for adapting to change, regardless if it comes from within the company in the form of product requirements, or from external factors such as Apple’s announcements.
The third and final step for preparing to significant shifts in the industry is to maintain low coupling between your frameworks and packages as you implement new features.
As professional iOS developers, we write software to solve problems and improve the lives of our customers. Although it can be appealing to adopt the latest technologies, we need to remember that we have a responsibility to our customers to strive to provide them with the best possible experience first.
A smooth migration (supported by modularity!) goes a long way in helping you to adopt new technologies and features that make your app great with high speed, low cost, and confidence.