I have a class which up until now used the UIApplication.shared
as a default argument during it's initialisation. The simplified version of it:
final class MyClass {
private var application: UIApplication
init(application: UIApplication = UIApplication.shared) {
self.application = application
}
}
With Strict Concurrency Checking
using the Complete
option now gives the following error:
Main actor-isolated class property 'shared' can not be referenced from a non-isolated context
It makes sense since UIApplication
is marked as @MainActor
hence, all it's properties are async:
@available(iOS 2.0, *)
@MainActor open class UIApplication : UIResponder {
open class var shared: UIApplication { get }
...
}
I see two solutions for this:
#Solution1
Mark the property as optional and initialise it during the init
. In some cases it may seem as a good idea, but with this we loose the ability to dependency inject the property and make it testable. Also using optionals further when we know the property is indeed initialised is uncomfortable:
final class MyClass {
private var application: UIApplication?
@MainActor
init() {
self.application = UIApplication.shared
}
}
#Solution2
Let's make the caller responsible for initialising the class on the @Mainactor
:
final class MyClass {
private var application: UIApplication
init(application: UIApplication) {
self.application = application
}
// Just to present what the caller would do
@MainActor
func option1ToInit() {
_ = MyClass(application: UIApplication.shared)
}
// Just to present what the caller would do
func option2ToInit() {
Task {
_ = await MyClass(application: UIApplication.shared)
}
}
}
The second solution seems to work nicely, however I need to do a bit of refactor work so all the callers are executing on the @MainActor
. I was wondering if there is any other option I am missing here.
If you can add @MainActor
to init (which it seems you can from your description), then you just need to move the accessing of .shared
into the init:
@MainActor
init(application: UIApplication? = nil) {
self.application = application ?? .shared
}
UPDATE: SE-0411 fixes this limitation. You can enable it in Swift 5.10 using the upcoming feature IsolatedDefaultValues.