Swift 6 concurrency - Xcode 16b5 - init() is always nonisolated?

I noticed a change from previous betas when I attempted to compile my code with Xcode 16 beta 5.

If I have a class that is MainActor isolated, and it overrides init(), it looks like this init is always considered nonisolated now, even for a MainActor isolated class.

This was not the case before.

For example, I have a class that inherits from NSScroller. It defines a default initializer init() which calls the designated initializer - super.init(frame:CGRect()). This is apparently not allowed right now. If that's the case, how can one even default-initialize a class??? It also doesn't let me initialize other properties, since everything is MainActor isolated.

Here's an abbreviated example.

class MyCustomScroller : NSScroller {

init () {
super.init(frame: CGRect())
scrollerStyle = .overlay
alphaValue = 0
}

}

This was allowed in prior betas. However, in beta 5, all 3 lines of my init generates an error such as: Call to main actor-isolated initializer 'init(frame:)' in a synchronous nonisolated context or Main actor-isolated property 'scrollerStyle' can not be mutated from a nonisolated context

Is it just no longer possible to default-initialize MainActor classes, such as NSViews, now?

The first line is particularly problematic because if I change it to just call super.init() then I get this error instead:

Must call a designated initializer of the superclass 'NSScroller'

You must call the designated initializer ... oh wait, we won't let you. Too bad.

Answered by arungj in 800222022

In MyCustomScroller, instead of a custom init, overriding the init with a default value worked for me.

override init(frame: CGRect = .zero) {
   super.init(frame: frame)
}

I'm also having problems with NSObjects in 16b5.

With complete concurrency checking enabled but Swift 5 language mode, the following generates 1 warning and 1 error:

import Foundation
import UIKit

@MainActor class TestObject: NSObject {
    
    let testView = UIView(frame: .zero)
    
    private func doSomething() {
        
    }
    
    override init() {
        // [ERROR] Property 'self.testView' not initialised at super.init call 

        super.init()    
        
         // [WARNING] Call to main actor-isolated instance method 'doSomething()' in a synchronous nonisolated context; this is an error in the Swift 6 language mode

        doSomething()
    }
}

So, (1) what's up with the let call not instantiating before the init() is called?

and (2) how come the override method is now reverting back to the solution of the function being overridden rather than the stated actor/isolation of the object it's being defined in?

I really hope Quin's about to put me straight.

And another similar warning with overriden functions (nos NS-based this time:

import Foundation
import UIKit

class TestListCell: UICollectionViewCell {
    
    private func doSomething() {
        
    }
    
    override func awakeFromNib() {

        // [WARNING] Call to main actor-isolated instance method 'doSomething()' in a synchronous nonisolated context; this is an error in the Swift 6 language mode

        doSomething()
    }    
}```




Accepted Answer

In MyCustomScroller, instead of a custom init, overriding the init with a default value worked for me.

override init(frame: CGRect = .zero) {
   super.init(frame: frame)
}

Hey, that's brilliant! I'm going to do that, instead. Especially since I already have an implementation of init(frame: CGRect) that also inits my properties (in the same way as the default initializer did). Using a default value fixes the issue and reduces the code.

This still feels like a Swift bug to me, but that fix works great.

The init() problem must have just been a bug. It's gone again in 16.0.beta6 which was just released today.

The awakeFromNib problem is still there- I just moved all my initialization code to other places (view lifecyle calls). Most of it already is.

Swift 6 concurrency - Xcode 16b5 - init() is always nonisolated?
 
 
Q