Non-sendable type 'Logger' in conformance of main actor-isolated property 'logger' to protocol requirement cannot cross actor boundary

Hi,

In Build Settings when I set Strict Concurrency Checking to Complete and compile the code below I get a warning.

Warning:

Non-sendable type 'Logger' in conformance of main actor-isolated property 'logger' to protocol requirement cannot cross actor boundary

Code:

import Foundation
import os

protocol P1 {
    var logger: Logger { get }
}

@MainActor
class Model: P1 {
    //Warning: Non-sendable type 'Logger' in conformance of main actor-isolated property 'logger' to protocol requirement cannot cross actor boundary
    let logger = Logger(subsystem: "sys1",category: "cat1")
}

Build Setting

  • In Build Settings set Strict Concurrency Checking to Complete

Questions:

  1. What should I do to resolve this warning?
  2. Or is this a bug in the os framework?

My Observation

  • Instead of @MainActor class if I used actor, then I could resolve the issue by adding a requirement that P1 must conform to Actor however I am not sure what must be done for @MainActor

Looks like if there is a @MainActor conforming to the protocol we could do on elf the options:

Option 1 (If main actor needs to be applied to the entire protocol)

@MainActor
protocol P1 {
    var logger: Logger { get }
}

Option 2 (If main actor needs to be applied only to one property)


protocol P1 {
    @MainActor var logger: Logger { get }
}

Note:

  • Looks like Actor is a protocol that can be used if we have a protocol that is conformed by actors.
  • Looks like @MainActor doesn't conform to Actor and hence we need to apply the attribute @MainActor

Another option is:

@MainActor
class Model: P1 {
    nonisolated let logger = Logger(subsystem: "sys1",category: "cat1")
}

The nonisolated modifier means that P1.logger is not isolated to the main actor like the rest of the class. This is required to fulfill the protocol requirement, which has no indication of actor isolation.

As you mention, another way to solve this issue is to make the requirement isolated, which you achieved both implicitly through @MainActor protocol P1 and explicitly @MainActor var logger. In this case, you isolate to the provided MainActor.

Lastly, actors have implicit isolation (to themselves). Writing actor Model { var logger: Logger } means you're allowed to access logger synchronously only within the actor Model. Because you (again) deal with isolation, you must somehow indicate that in your protocol. By writing P1: Actor you're saying that every protocol requirement is implicitly isolated to the Self actor.

I know this discussion of isolated, implicit isolated, and non-isolated can be confusing but I hope I helped. Let me know if anything was unclear.

3 Likes

Thanks a lot @filip-sakel for showing the non-isolated option

Yeah it is a bit tricky but I am slowly starting to understand, thanks!

1 Like