Why can't I send an NSImage across actor boundaries?

@OneSadCookie not sure what I'm missing then.

This is my function

public func logoImage(size:LogoSize) -> sending HSImage? {

    var logoPath:Path?
    
    switch size {
    case .large:
        logoPath = path(for: .logo)
    case .small:
        logoPath = path(for: .logoSmall)
    case .preferSmall:
        logoPath = path(for: .logoSmall) ?? path(for: .logo)
    }
    
    guard let logoPath else {
        return nil
    }
    
    return HSImage(contentsOfFile: logoPath.string)
}

I'm using XCode 16.2
I'm in Swift 5 mode with RBI explicitly turned on
I still get the warning.

Non-sendable type 'HSImage?' (aka 'Optional') returned by call to main actor-isolated function cannot cross actor boundary; this is an error in the Swift 6 language mode

You mention Optional<T> is Sendable iff T is Sendable but I'm not sure why that is relevant. The point here is that T is not Sendable...

I don't think that warning comes from the code you've posted, though?

I'd want to see

  • the type containing the code you posted
  • the code calling logoImage
  • the type containing the code calling logoImage
  • exactly which line the warning is from

I guess?

You mention Optional<T> is Sendable iff T is Sendable but I'm not sure why that is relevant. The point here is that T is not Sendable ...

The point is to say that the region-based analysis only cares about Sendable, and Optional doesn't interfere with that. Optional<T> behaves exactly the same as T.

1 Like

The warning appears at the caller of that code

@Observable
@MainActor
public class IdentityAssetManager {

   ...

    public func logoImage(size:LogoSize) -> sending HSImage? {

        var logoPath:Path?
        
        switch size {
        case .large:
            logoPath = path(for: .logo)
        case .small:
            logoPath = path(for: .logoSmall)
        case .preferSmall:
            logoPath = path(for: .logoSmall) ?? path(for: .logo)
        }
        
        guard let logoPath else {
            return nil
        }
        
        return HSImage(contentsOfFile: logoPath.string)
    }



which is then called from


class CompositionImageLayerBuilder {

  ...

func build(for plan: RenderPlan) async throws -> CALayer {

    let introLength = plan.preRenderedIntroLength

    let endTitleRange = CMTimeRange(start: composition.duration - plan.outroLength,
                                    duration: plan.outroLength)

    if showLogoWatermarkOnEdit {
        let watermarkRange = CMTimeRange(start: introLength,
                                         end: composition.duration - endTitleRange.duration)
        await addWatermark(range: watermarkRange)
    }

    if !plan.hasPreRenderedOutro {
        let dzText = await logoHeadline
        let subheadline = await logoSubheadline
        
  //ERROR HERE on logoImage
        let dzImage = await dropzoneProvider.assetManager?.logoImage(size:.large)

        try addEndLogo(
            image: dzImage,
            headline: dzText,
            subheadline: subheadline,
            background: .white,
            logoRange: endTitleRange
        )
    }

    let logoRange = CMTimeRange(start: composition.duration - fffLogoDuration,
                                    duration: fffLogoDuration)
    try addFFFLogo(range: logoRange)

    return parentLayer
}

(sorry for multiple edits - battling mobile internet while posting)