Call to main actor-isolated instance method [...] in a synchronous nonisolated context; this is an error in the Swift 6 language mode

Hi all, I need some guidance to understand this warning I am getting while migrating the app to Swift 6. I've enabled the Struct Concurrency Checking to Complete. I have the following SwiftUI view:

struct HolidaysListView: View {
    let holidays: [Holiday]
    @Binding var path: NavigationPath
    
    var body: some View {
        VStack {
            GeometryReader { proxy in
                let size = proxy.size
                ScrollView(.horizontal) {
                    HStack(spacing: 0) {
                        ForEach(holidays) { holiday in
                            HolidayListViewItem(holiday: holiday, path: $path)
                                .padding(.horizontal)
                                .frame(width: size.width)
                                .visualEffect { content, geometryProxy in
                                    content
                                        .scaleEffect(scale(geometryProxy), anchor: .trailing)
                                        .rotationEffect(rotation(geometryProxy))
                                        .offset(x: minX(geometryProxy))
                                        .offset(x: excessMinX(geometryProxy))
                                }
                                .zIndex(holidays.zIndex(of: holiday))
                        }
                    }
                    .padding(.vertical)
                }
                .scrollTargetBehavior(.paging)
                .scrollIndicators(.hidden)
            }
            .aspectRatio(contentMode: .fit)
        }
    }
    
    private func minX(_ proxy: GeometryProxy) -> CGFloat {
        let minX = proxy.frame(in: .scrollView(axis: .horizontal)).minX
        return minX < 0 ? 0 : -minX
    }
    
    private func progess(_ proxy: GeometryProxy) -> CGFloat {
        let maxX = proxy.frame(in: .scrollView(axis: .horizontal)).maxX
        guard let width = proxy.bounds(of: .scrollView(axis: .horizontal))?.width else {
            return 0
        }
        let visibleCards = CGFloat(2)
        return min((maxX / width) - 1, visibleCards)
    }
    
    private func scale(_ proxy: GeometryProxy) -> CGFloat {
        let scaleFactor = 0.1
        return 1 - (progess(proxy) * scaleFactor)
    }
    
    private func excessMinX(_ proxy: GeometryProxy) -> CGFloat {
        let offset: CGFloat = 10
        return progess(proxy) * offset
    }
    
    private func rotation(_ proxy: GeometryProxy) -> Angle {
        let rotation: CGFloat = 5
        return Angle(degrees: progess(proxy) * rotation)
    }
}

And in the visualEffect modifier, I am getting 4 warnings like this:

It seems GeometryProxy is not Sendable.
I am lost on what to do here. Any feedback and explanations are much appreciated.

By default, methods of any struct conforming to View inherit the Main Actor isolation. So here scale, rotation, minX and excessMinX are implicitly @MainActor as well.

You don't want that because visualEffect's closure is not Main Actor isolated, so attempting to call any of those methods from within said closure raises a "Call to main actor-isolated instance method 'rotation' in a synchronous nonisolated context" warning.

All you need to do is mark the methods as nonisolated:

nonisolated private func minX(_ proxy: GeometryProxy) -> CGFloat { ... }
  
nonisolated private func progess(_ proxy: GeometryProxy) -> CGFloat { ... }
  
nonisolated private func scale(_ proxy: GeometryProxy) -> CGFloat { ... }
  
nonisolated private func excessMinX(_ proxy: GeometryProxy) -> CGFloat { ... }
  
nonisolated private func rotation(_ proxy: GeometryProxy) -> Angle { ... }

By removing this implicit @MainActor isolation, these methods will be able to be called from inside visualEffect { ... }. It's safe to pass a GeometryProxy to them because you're not crossing an actor boundary by doing so.

1 Like

Thanks @Andropov
I appreciate the time to answer my question. Need to learn better Concurrency.

1 Like