Swift 6 conversion for IBOutlet

I'm struggling to convert Swift 5 to Swift 6.

As advised in doc, I first turned strict concurrency ON. I got no error.

Then, selected swift6… and problems pop up.

I have a UIViewController with

  • IBOutlets: eg a TextField.
  • computed var eg duree
  • func using UNNotification: func userNotificationCenter

I get the following error in the declaration line of the func userNotificationCenter:

Main actor-isolated instance method 'userNotificationCenter(_:didReceive:withCompletionHandler:)' cannot be used to satisfy nonisolated requirement from protocol 'UNUserNotificationCenterDelegate'

So, I declared the func as non isolated.

This func calls another func func2, which I had also to declare non isolated.

Then I get error on the computed var used in func2

Main actor-isolated property 'duree' can not be referenced from a nonisolated context

So I declared duree as nonsilated(unsafe).

Now comes the tricky part.

The computed var references the IBOutlet dureeField

if dureeField.text == "X"

leading to the error

Main actor-isolated property 'dureeField' can not be referenced from a nonisolated context

So I finally declared the class as mainActor and the textField as nonisolated

    @IBOutlet nonisolated(unsafe) weak var dureeField  : UITextField!  

That silences the error (but declaring unsafe means I get no extra robustness with swift6) just to create a new one when calling dureeField.text:

Main actor-isolated property 'text' can not be referenced from a nonisolated context

Question: how to address properties inside IBOutlets ? I do not see how to declare them non isolated and having to do it on each property of each IBOutlet would be impracticable.

The following did work, but will make code very verbose:

if  MainActor.assumeIsolated({dureeField.text == "X"}) {

So I must be missing something.

MainActor.assumeIsolated seems the simplest way to go.

I have noticed an important point when converting to Swift 6 which may help others.

At some point, I had nearly 200 errors… Don't panic they say…

Problem was that errors kept coming and disappearing. Just as if the compiler was a bit lost (and me with him) with all the changes occurring on classes or func (such as declaring MainActor or non isolated). This made error correction pretty erratic.

I did a Clean build folder, and the number of errors dropped to less than 10.

I suspect that the root cause here is the declared isolation of userNotificationCenter(_:didReceive:withCompletionHandler:). I don’t known the User Notifications framework well, but my best guess is that this method is always called on the main thread. If that’s the case, you can conform to the protocol like so:

final class MainViewController: UITableViewController, UNUserNotificationCenterDelegate {

    …
    
    nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
        let actionIdentifier = response.actionIdentifier
        await self.notificationDidReceiveResponse(actionIdentifier: actionIdentifier)
    }
    
    private func notificationDidReceiveResponse(actionIdentifier: String) {
        print(actionIdentifier)
    }
}

The tricky part here is that UNNotificationResponse is not sendable, so you have to extract the relevant things you need from response into sendable values before calling notificationDidReceiveResponse(…). I’m illustrating that here with actionIdentifier, but it’s likely you’ll need more than that.

The overall goal here is to use the minimum amount of code to get you from this potentially non-isolated context to a main-actor-isolated context. That ensures that the bulk of your view controller is isolated to the main actor, which is the way that you want it. For example, avoid marking an IBOutlet as non-isolated because those really are main-thread-only values.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks Quinn.

I removed the nonisolated for IBOutlet.

But, as shown in your example, I had to mark all func în a class that uses UNUserNotificationCenterDelegate as nonIsolated, such as

nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)

I also note in your example that you marked response for didReceive as async. I tried to, but got the error:

Instance method 'userNotificationCenter(_:didReceive:withCompletionHandler:)' nearly matches optional requirement 'userNotificationCenter(_:didReceive:withCompletionHandler:)' of protocol 'UNUserNotificationCenterDelegate'

with the advice to

Make 'userNotificationCenter_:didReceive:withCompletionHandler:)' private to silence this warning

Converting to Swift 6 is definitely not an easy ride…

I'll do extensive testing.

Converting to Swift 6 is definitely not an easy ride…

Well, we’re actively working to improve that.

Speaking of that, what version of Xcode are you testing this on? I want to play around with some specific scenarios and it’d be good to know whether you’re testing on Xcode 16.4 or the latest Xcode 26.0 beta.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo that was when testing with Swift6 and Xcode 16.4.

But after encapsulating the code using IBOutlet in

MainActor.assumeIsolated({ 
  // using textField.text
})

I could remove the nonIsolated declaration for IBOutlet and get it working.

Unfortunately, I've not kept a version to reproduce the error.

In any case, I will not switch to Swift6 before using Xcode 26.

Swift 6 conversion for IBOutlet
 
 
Q