Swift 6 has been out there to us for the higher a part of a 12 months now, and increasingly more groups are contemplating or taking a look at migrating to the Swift 6 language mode. This sometimes entails attempting to activate the language mode or turning on strict concurrency, seeing a complete bunch of warnings or errors, after which deciding that at this time is just not the day to proceed with this migration.
In the present day I want to suggest an strategy to how one can plan your migration in a method that received’t scare you out of trying the migration earlier than you’ve even began.
Earlier than you undergo this complete publish anticipating me to let you know tips on how to go to Swift 6 inside a matter of days or perhaps weeks, I am afraid I will should disappoint you.
Migrating to Swift 6, for lots of apps, goes to be a really gradual and prolonged course of and it is actually a course of that you do not wish to rush.
Taking an preliminary stock
Earlier than you begin to migrate your codebase, I’d extremely suggest that you simply take stock of the state of your codebase. Because of this it’s best to check out how modularized your codebase is, which dependencies you have got in your codebase, and perhaps most significantly how a lot concurrency you’re actually utilizing proper now. Learn the way typically you’re explicitly, and purposefully you’re leaving the primary thread. And attempt to perceive how a lot of your code will run on the primary thread.
You also needs to have a look at your group and work out how up-to-date your group is, how comfy they’re with Swift concurrency already. Ultimately, the whole group will want to have the ability to work on and along with your Swift 6 codebase.
On a code degree, it is important to know how a lot concurrency you really need as a result of Swift concurrency is, by design, going to introduce loads of concurrency into your app the place perhaps you do not really need all of that concurrency. That’s why it’s so vital so that you can determine the quantity of concurrency you’ll require beforehand by analyzing what you have got now.
For instance, when you have a view and you’ve got a view mannequin, and that view mannequin talks to a different layer, then in all probability you might be doing many of the work on the primary thread proper now.
When you hit your networking layer, your community calls will run someplace else, and when your networking associated features invoke their callbacks, these will sometimes run on a background thread, and then you definitely come again to the primary thread to replace your UI.
On this situation, you do not want loads of concurrency; in reality, I’d say that you do not want concurrency past what URLSession gives in any respect. So when you’re adopting Swift Concurrency, you’ll wish to perceive how one can construction your code to not depart the primary thread for each async name.
You may have already got adopted async-await, and that is fully tremendous—it in all probability implies that you do have extra concurrency than you really need. Each nonisolated async operate that you simply write will, by default, run on a background thread. You don’t at all times want this; you’ll almost certainly wish to explicitly isolate a few of your work to the primary actor to stop leveraging concurrency in locations the place it’s merely not benefitting you.
You will additionally wish to just remember to perceive how dependent or how coupled your codebase is as a result of the extra coupling you have got and the much less abstractions and modularization you have got, the extra complexities you may run into. Understanding your codebase deeply is a prerequisite to shifting to Swift 6.
When you perceive your codebase, you may wish to have a look at modularizing. I’d say that is the best choice. It does make migrating a bit bit simpler.
So let’s speak about modularization subsequent.
Modularizing your codebase
If you migrate to Swift 6, you may discover that loads of objects in your code are being handed from one place to a different, and whenever you begin to introduce concurrency in a single a part of the code, you’re basically compelled emigrate something that depends upon that a part of your codebase.
Having a modularized codebase means which you could take your codebase and migrate it over time. You may transfer part by part, relatively than being compelled to maneuver the whole lot .
You should use options like @preconcurrency to guarantee that your app can nonetheless use your Swift 6 modules with out working into all types of isolation or sendability warnings till your app additionally opts in to strict concurrency.
When you do not wish to modularize your codebase otherwise you really feel your codebase is method too small to be modularized, that is fully tremendous. I am simply saying that the smaller your elements are, the better your migration goes to be.
As soon as you realize the state your codebase is in and you’re feeling comfy with how the whole lot is, it is time to activate strict concurrency checks.
Turning on strict concurrency checks
Earlier than you activate Swift 6 language mode, it is strongly recommended to activate strict concurrency checking for the modules that you simply wish to migrate. You are able to do this for each SPM and in Xcode on your app goal.
I’d suggest to do that on a module by module foundation.
So if you wish to refactor your fashions package deal first, activate strict concurrency checks on your mannequin package deal, however not but on your app. Turning on strict concurrency for just one module lets you work on that package deal with out forcing your app to choose into all the sendability and isolation checks associated to the package deal you’re refactoring.
Having the ability to migrate one package deal at a time is tremendous helpful as a result of it makes the whole lot rather a lot simpler to cause about because you’re reasoning about smaller bits of your code.
After getting your strict concurrency checks turned on you are going to see a complete bunch of warnings for the packages and targets the place you’ve got enabled strict concurrency and you can begin fixing them. For instance, it’s doubtless that you’re going to run into points like fundamental actor remoted objects to sendable closures.
You will wish to just remember to perceive these errors earlier than you proceed.
You wish to guarantee that your entire warnings are resolved earlier than you activate Swift 6 language mode, and also you wish to just remember to’ve received a extremely good sense of how your code is meant to work.
The toughest half in fixing your strict concurrency warnings is that making the compiler completely happy typically simply is not sufficient. You will continuously wish to just remember to really cause concerning the intent of your code relatively than simply making the compiler completely happy.
Contemplate the next code instance:
func loadPages() {
for web page in 0..<10 {
loadPage(web page) { end in
// use consequence
}
}
}
We’re iterating over an inventory of numbers and we’re making a bunch of community calls. These community calls occur concurrently and our operate would not watch for all of them to finish. Now, the quickest strategy to migrate this over to Swift concurrency is likely to be to jot down an async operate and a for loop that appears like this:
func loadPages() async throws {
for web page in 0..<10 {
let consequence = attempt await loadPage(web page)
// use consequence
}
}
The which means of this code has now modified completely. We’re making community calls one after the other and the operate would not return till each name is full. If we do wish to introduce Swift concurrency right here and preserve the identical semantics we must create unstructured duties for each single community name or we may use a process group and kick off all our community calls in parallel that method.
Utilizing a process group would change the best way this operate works, as a result of the operate must watch for the duty group to finish relatively than simply letting unstructured duties run. On this refactor, it’s essential to know what structured concurrency is and when it is sensible to create unstructured duties.
You are having to consider what the intent of the code is earlier than you migrate after which additionally how and if you wish to change that in your migration. If you wish to preserve the whole lot the identical, it is typically not sufficient to maintain the compiler completely happy.
Whereas educating Groups about Swift Concurrency, I discovered it actually vital to know precisely which instruments you have got on the market and to consider how try to be reasoning about your code.
As soon as you’ve got turned on Swift Concurrency checks, it is time to guarantee that your complete group is aware of what to do.
Making certain your group has all of the information they want
I’ve seen a number of corporations try migrations to SwiftUI, Swift Knowledge, and Swift Concurrency. They typically take approaches the place a small group does all of the legwork when it comes to exploring and studying these applied sciences earlier than the remainder of the group is requested to be taught them too and to undertake them. Nonetheless, this typically means that there is a small group within the corporate that you may contemplate to be consultants. They’re going to have had entry to sources, they will have had time to coach, and as soon as they give you the final large image of how issues needs to be executed, the remainder of the group sort of has to be taught on the job. Generally this works effectively, however typically this breaks down as a result of the remainder of the group merely wants to totally perceive what they’re coping with earlier than they’ll successfully be taught.
So I at all times suggest if you wish to migrate over to Swift Concurrency have your group enroll in one in every of my workshops or use my books or my course or discover some other useful resource that may train the group the whole lot they should know. It is actually not trivial to choose up Swift Concurrency, particularly not if you wish to go into strict concurrency mode. Writing async-await features is comparatively straightforward, however understanding what occurs is actually what you want if you happen to’re planning emigrate and go all-in on concurrency.
As soon as you’ve got determined that you will go for Swift 6 and did you wish to degree up your group’s concurrency abilities be sure you really give all people on the group an opportunity to correctly be taught!
Migrating from the surface in
As soon as you’ve got began refactoring your packages and it is time to begin working in your app goal I discovered that it actually is sensible emigrate from the surface in. You can additionally work from the within out and ultimately, it actually depends upon the place you wish to begin. That mentioned, I typically like to begin within the view layer as soon as all of the back-end stuff is finished as a result of it helps me decide at which level within the app I wish to depart the primary actor (or when yo apply a fundamental actor annotation to remain on the primary actor).
For instance, if you happen to’re utilizing MVVM and you’ve got a view mannequin that holds a bunch of features, the place ought to these features run?
That is the place the work that you simply did up entrance comes into play since you already know that within the outdated method of doing issues the view mannequin would run its features on the primary thread. I’d extremely suggest that you don’t change this. In case your view mannequin used to run on the primary thread which is just about normal, preserve it that method.
You will wish to apply a fundamental actor annotation to your view mannequin class.
This isn’t a nasty factor by any means, it is not a hack both. It is a method so that you can be certain that you are not switching isolation contexts on a regular basis. You actually do not want a ton of concurrency in your app code.
So so that you can default your view fashions and perhaps another objects in your code base to the primary actor merely makes loads of sense. When you begin migrating like this you may work out that you simply actually did not want that a lot concurrency which you already ought to know as a result of that is what you found out early on into course of.
That is additionally the place you begin to encounter warnings which can be associated to sendability and isolation contexts. When you begin to see these warnings and errors, you resolve that the mannequin ought to or should not be sendable relying on whether or not the change of isolation context that’s inflicting the warning is predicted.
You may resolve sendability issues with actors. That mentioned, making issues actors is normally not what you are searching for particularly when it is associated to fashions.
Nonetheless, whenever you’re coping with a reference sort that has mutable state, that is the place you may introduce actors. It’s all about determining whether or not you have been anticipating to make use of that sort in a number of isolation contexts.
Having to deeply cause about each error and warning can typically really feel tedious as a result of it actually slows you down. You may simply make one thing sendable, you may simply make one thing an actor, and it would not affect your code that a lot. However you might be introducing loads of complexity into your codebase whenever you’re introducing isolation contexts and whenever you’re introducing concurrency.
So once more, you actually wish to just remember to restrict the quantity of concurrency in your app. You sometimes do not want loads of concurrency inside an software. I am unable to stress this sufficient.
Pitfalls, caveats, and risks
Migrating to Swift 6 positively comes with its risks and uncertainties. When you’re migrating the whole lot , you are going to be embarking on an enormous refactor that may contain touching nearly each single object in your code. When you introduce actors the place they actually should not belong, you all of a sudden have the whole lot in your code turning into concurrent as a result of interacting with actors is an asynchronous proces.
When you did not observe the steps on this weblog publish, you are in all probability going to have asynchronous features all over, and so they is likely to be members of lessons or your view or anything. A few of your async features are going to be remoted to the primary actor, however most of them can be non-isolated by default, which implies that they’ll run wherever. This additionally implies that if you happen to cross fashions or objects out of your view to your few mannequin to another place that you simply’re skipping isolation contexts on a regular basis. Generally that is fully tremendous, and the compiler will work out that issues are literally protected, however in loads of instances, the compiler goes to complain about this, and you may be very pissed off about this as a result of you haven’t any thought what’s incorrect.
There’s additionally the matter of interacting with Apple’s code. Not all of Apple’s code is essentially Swift 6 suitable or Swift 6 pleasant. So that you may end up having to jot down workarounds for interacting with issues like a CLLocationManagerDelegate
or different objects that come from Apple’s frameworks. Generally it is trivial to know what to do when you totally perceive how isolation works, however loads of the instances you are going to be left guessing about what makes probably the most sense.
That is merely unavoidable, and we’d like Apple to work on their code and their annotations to guarantee that we will undertake Swift 6 with full confidence.
On the similar time, Apple is taking a look at Swift as a language and determining that Swift 6 is actually not within the place the place they need it to be for common adoption.
When you’re adopting Swift 6 proper now, there are some issues which may change down the road. You must be prepared to cope with that. When you’re not prepared to cope with that, I’d suggest that you simply go for strict concurrency and do not go all-in on Swift 6 as a result of issues may change down the road and you do not wish to be doing a ton of labor that seems to be out of date. A pair variations of Swift down the road, and we’re in all probability speaking months, not years, earlier than this occurs.
In Abstract
General, I feel adopting Swift 6 is a large enterprise for many groups. If you have not began already and also you’re about to begin now, I’d urge you to take it gradual – take it straightforward and just remember to perceive what you are doing as a lot as doable each step of the best way.
Swift concurrency is fairly sophisticated, and Apple remains to be actively engaged on bettering and altering it as a result of they’re nonetheless studying about issues which can be inflicting issues for individuals on a regular basis. So for that cause, I am not even certain that migrating to Swift 6 needs to be one in every of your major targets at this cut-off date.
Understanding the whole lot round Swift 6 I feel is extraordinarily helpful as a result of it does allow you to to jot down higher and safer code. Nonetheless, I do imagine that sticking with the Swift 5 language mode and going for strict concurrency might be your most secure guess as a result of it lets you write code that will not be totally Swift 6 compliant however works fully tremendous (no less than you may nonetheless compile your challenge even when you have a complete bunch of warnings).
I’d like to know your ideas and progress on migrating to Swift 6. In my workshops I at all times hear actually cool tales about corporations which can be engaged on their migration and so when you have tales about your migration and your journey with Swift 6, I’d love to listen to that.