Search code examples
iosswift

Referenced to captured var in concurrently-execute code?


I have the following code:

import SwiftUI
import Observation

struct ContentView: View {

    @State var vm = ViewModel()
    
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text("Hello, world!")
        }
        .padding()
    }
    
}

@Observable class ViewModel {
    
    struct TestInfo {
        var myId:Int
    }
    
    @MainActor var info:TestInfo?
    
    func setupInfo() async {
        
        var tmp:TestInfo?
        
        await CoreData.shared.context.perform {

            /// some core data work and then we set tmp
            tmp = TestInfo(myId: ....)
        }
        
        
        await MainActor.run {
            info = tmp /// ERROR: Reference to captured var 'tmp' in concurrently-executing code
        }
        
    }
    
}

I would like to know how to fix this error. I setup tmp within the core data context closure and would like to then set info to tmp.


Solution

  • Your ViewModel should be annotated @MainActor since it drives the UI. This will ensure that setupInfo is on the main actor, and remove the need for the MainActor.run that is causing trouble.

    Furthermore, you should not set up a tmp and modify it elsewhere. Instead, let perform return your value:

    self.info = CoreData.shared.context.perform {
         /// some core data work
         return TestInfo(myId: ....)
    }
    

    Altogether it would look like this:

    @MainActor @Observable class ViewModel {
    
        struct TestInfo {
            var myId:Int
        }
    
        var info:TestInfo?
    
        func setupInfo() async {
            info = await CoreData.shared.context.perform {
    
                /// some core data work and then we set tmp
                tmp = TestInfo(myId: ....)
            }
        }
    }