Fixing actor-isolated protocol conformance associated errors in Swift 6.2 – Donny Wals

0
1
Fixing actor-isolated protocol conformance associated errors in Swift 6.2 – Donny Wals


Revealed on: June 27, 2025

Swift 6.2 comes with a number of high quality of life enhancements for concurrency. Considered one of these options is the flexibility to have actor-isolated conformances to protocols. One other characteristic is that your code will now run on the principle actor by default.

This does imply that typically, you’ll run into compiler errors. On this weblog publish, I’ll discover these errors, and how one can repair them if you do.

Earlier than we do, let’s briefly discuss actor-isolated protocol conformance to grasp what this characteristic is about.

Understanding actor-isolated protocol conformance

Protocols in Swift can require sure features or properties to be nonisolated. For instance, we will outline a protocol that requires a nonisolated var identify like this:

protocol MyProtocol {
  nonisolated var identify: String { get }
}

class MyModelType: MyProtocol {
  var identify: String

  init(identify: String) {
    self.identify = identify
  }
}

Our code is not going to compile for the time being with the next error:

Conformance of 'MyModelType' to protocol 'MyProtocol' crosses into principal actor-isolated code and may trigger knowledge races

In different phrases, our MyModelType is remoted to the principle actor and our identify protocol conformance isn’t. Which means that utilizing MyProtocol and its identify in a nonisolated manner, can result in knowledge races as a result of identify isn’t really nonisolated.

Once you encounter an error like this you may have two choices:

  1. Embrace the nonisolated nature of identify
  2. Isolate your conformance to the principle actor

The primary resolution often implies that you don’t simply make your property nonisolated, however you apply this to your whole sort:

nonisolated class MyModelType: MyProtocol {
  // ...
}

This would possibly work however you’re now breaking out of principal actor isolation and doubtlessly opening your self as much as new knowledge races and compiler errors.

When your code runs on the principle actor by default, going nonisolated is usually not what you need; every thing else continues to be on principal so it is sensible for MyModelType to remain there too.

On this case, we will mark our MyProtocol conformance as @MainActor:

class MyModelType: @MainActor MyProtocol {
  // ...
}

By doing this, MyModelType conforms to my protocol however solely after we’re on the principle actor. This robotically makes the nonisolated requirement for identify pointless as a result of we’re at all times going to be on the principle actor after we’re utilizing MyModelType as a MyProtocol.

That is extremely helpful in apps which are principal actor by default since you don’t need your principal actor sorts to have nonisolated properties or features (often). So conforming to protocols on the principle actor makes a whole lot of sense on this case.

Now let’s take a look at some errors associated to this characteristic, we could? I initially encountered an error round my SwiftData code, so let’s begin there.

Fixing Principal actor-isolated conformance to ‘PersistentModel’ can’t be utilized in actor-isolated context

Let’s dig proper into an instance of what can occur if you’re utilizing SwiftData and a customized mannequin actor. The next mannequin and mannequin actor produce a compiler error that reads “Principal actor-isolated conformance of ‘Train’ to ‘PersistentModel’ can’t be utilized in actor-isolated context”:

@Mannequin
class Train {
  var identify: String
  var date: Date

  init(identify: String, date: Date) {
    self.identify = identify
    self.date = date
  }
}

@ModelActor
actor BackgroundActor {
  func instance() {
    // Name to principal actor-isolated initializer 'init(identify:date:)' in a synchronous actor-isolated context
    let train = Train(identify: "Operating", date: Date())
    // Principal actor-isolated conformance of 'Train' to 'PersistentModel' can't be utilized in actor-isolated context
    modelContext.insert(train)
  }
}

There’s really a second error right here too as a result of we’re calling the initializer for train from our BackgroundActor and the init for our Train is remoted to the principle actor by default.

Fixing our downside on this case implies that we have to enable Train to be created and used from non-main actor contexts. To do that, we will mark the SwiftData mannequin as nonisolated:

@Mannequin
nonisolated class Train {
  var identify: String
  var date: Date

  init(identify: String, date: Date) {
    self.identify = identify
    self.date = date
  }
}

Doing it will make each the init and our conformance to PersistentModel nonisolated which suggests we’re free to make use of Train from non-main actor contexts.

Observe that this does not imply that Train can safely be handed from one actor or isolation context to the opposite. It simply implies that we’re free to create and use Train situations away from the principle actor.

Not each app will want this or encounter this, particularly if you’re operating code on the principle actor by default. In the event you do encounter this downside for SwiftData fashions, you need to most likely isolate the problematic are to the principle actor except you particularly created a mannequin actor within the background.

Let’s check out a second error that, so far as I’ve seen is fairly widespread proper now within the Xcode 26 beta; utilizing Codable objects with default actor isolation.

Fixing Conformance of protocol ‘Encodable’ crosses into principal actor-isolated code and may trigger knowledge races

This error is sort of attention-grabbing and I wonder if it’s one thing Apple can and may repair in the course of the beta cycle. That stated, as of Beta 2 you would possibly run into this error for fashions that conform to Codable. Let’s take a look at a easy mannequin:

struct Pattern: Codable {
  var identify: String
}

This mannequin has two compiler errors:

  1. Round reference
  2. Conformance of ‘Pattern’ to protocol ‘Encodable’ crosses into principal actor-isolated code and may trigger knowledge races

I’m not precisely certain why we’re seeing the primary error. I believe it is a bug as a result of it is not sensible to me for the time being.

The second error says that our Encodable conformance “crossed into principal actor-isolated code”. In the event you dig a bit deeper, you’ll see the next error as a proof for this: “Principal actor-isolated occasion methodology ‘encode(to:)’ can not fulfill nonisolated requirement”.

In different phrases, our protocol conformance provides a principal actor remoted implementation of encode(to:) whereas the protocol requires this methodology to be non-isolated.

The explanation we’re seeing this error just isn’t completely clear to me however there appears to be a mismatch between our protocol conformance’s isolation and our Pattern sort.

We will do certainly one of two issues right here; we will both make our mannequin nonisolated or constrain our Codable conformance to the principle actor.

nonisolated struct Pattern: Codable {
  var identify: String
}

// or
struct Pattern: @MainActor Codable {
  var identify: String
}

The previous will make it in order that every thing on our Pattern is nonisolated and can be utilized from any isolation context. The second possibility makes it in order that our Pattern conforms to Codable however solely on the principle actor:

func createSampleOnMain() {
  // that is wonderful
  let pattern = Pattern(identify: "Pattern Occasion")
  let knowledge = strive? JSONEncoder().encode(pattern)
  let decoded = strive? JSONDecoder().decode(Pattern.self, from: knowledge ?? Information())
  print(decoded)
}

nonisolated func createSampleFromNonIsolated() {
  // this isn't wonderful
  let pattern = Pattern(identify: "Pattern Occasion")
  // Principal actor-isolated conformance of 'Pattern' to 'Encodable' can't be utilized in nonisolated context
  let knowledge = strive? JSONEncoder().encode(pattern)
  // Principal actor-isolated conformance of 'Pattern' to 'Decodable' can't be utilized in nonisolated context
  let decoded = strive? JSONDecoder().decode(Pattern.self, from: knowledge ?? Information())
  print(decoded)
}

So usually talking, you don’t need your protocol conformance to be remoted to the principle actor on your Codable fashions should you’re decoding them on a background thread. In case your fashions are comparatively small, it’s probably completely acceptable so that you can be decoding and encoding on the principle actor. These operations must be quick sufficient normally, and sticking with principal actor code makes your program simpler to cause about.

One of the best resolution will rely in your app, your constraints, and your necessities. All the time measure your assumptions when doable and persist with options that give you the results you want; don’t introduce concurrency “simply to make sure”. In the event you discover that your app advantages from decoding knowledge on a background thread, the answer for you is to mark your sort as nonisolated; should you discover no direct advantages from background decoding and encoding in your app you need to constrain your conformance to @MainActor.

In the event you’ve applied a customized encoding or decoding technique, you could be operating into a special error…

Conformance of ‘CodingKeys’ to protocol ‘CodingKey’ crosses into principal actor-isolated code and may trigger knowledge races

Now, this one is a little bit trickier. When we’ve got a customized encoder or decoder, we would additionally need to present a CodingKeys enum:

struct Pattern: @MainActor Decodable {
  var identify: String

  // Conformance of 'Pattern.CodingKeys' to protocol 'CodingKey' crosses into principal actor-isolated code and may trigger knowledge races
  enum CodingKeys: CodingKey {
    case identify
  }

  init(from decoder: any Decoder) throws {
    let container = strive decoder.container(keyedBy: CodingKeys.self)
    self.identify = strive container.decode(String.self, forKey: .identify)
  }
}

Sadly, this code produces an error. Our conformance to CodingKey crosses into principal actor remoted code and which may trigger knowledge races. Often this is able to imply that we will constraint our conformance to the principle actor and this is able to remedy our concern:

// Principal actor-isolated conformance of 'Pattern.CodingKeys' to 'CustomDebugStringConvertible' can not fulfill conformance requirement for a 'Sendable' sort parameter 'Self'
enum CodingKeys: @MainActor CodingKey {
  case identify
}

This sadly doesn’t work as a result of CodingKeys requires us to be CustomDebugStringConvertable which requires a Sendable Self.

Marking our conformance to principal actor ought to imply that each CodingKeys and CodingKey are Sendable however as a result of the CustomDebugStringConvertible is outlined on CodingKey I believe our @MainActor isolation doesn’t carry over.

This may additionally be a tough edge or bug within the beta; I’m undecided.

That stated, we will repair this error by making our CodingKeys nonisolated:

struct Pattern: @MainActor Decodable {
  var identify: String

  nonisolated enum CodingKeys: CodingKey {
    case identify
  }

  init(from decoder: any Decoder) throws {
    let container = strive decoder.container(keyedBy: CodingKeys.self)
    self.identify = strive container.decode(String.self, forKey: .identify)
  }
}

This code works completely wonderful each when Pattern is nonisolated and when Decodable is remoted to the principle actor.

Each this concern and the earlier one really feel like compiler errors, so if these get resolved throughout Xcode 26’s beta cycle I’ll be sure to return again and replace this text.

In the event you’ve encountered errors associated to actor-isolated protocol conformance your self, I’d love to listen to about them. It’s an attention-grabbing characteristic and I’m attempting to determine how precisely it suits into the best way I write code.

LEAVE A REPLY

Please enter your comment!
Please enter your name here