Is there a way to get rid of a new concurrency warning on a @Published variable

614 views Asked by At

I'm currently working on a new iOS project. To be up to date with the new concurrency from Swift I have switched the Strict concurrency checking setting too "complete". This has of course sprung up lot's of warning and error that I was mostly able to fix.

Never the less there are a few cases that I can't figure out. My project structure is a very commun MVVM pattern in Swift/SwiftUI. Where my viewModels implement the ObservableObject and Sendable protocols.

They contain @Published variables that are update internally to refresh my associated SwiftUI views.

My problem is that I have concurrency warnings on these @Published variables and I can't figure out how to get rid of them except settings my viewmodels as @unchecked Sendable, which is not really recommended.

Following is an example of a viewModel and it's associated warning:

final class HomeViewModel: ObservableObject, Sendable {

    // The warning concerns this variable
    @Published private(set) var pageState: PageState<HomePageModel> = .loading

    private var reloadTask: Task<Void, Never>?

    init() {
        setUp()
    }

    deinit {
        reloadTask?.cancel()
    }

    func fetchHomeContent() async {
        if Task.isCancelled { return }
        do {
            XXXXX async execution happening here

            try Task.checkCancellation()
            let sortedSections = sections.sorted { $0.order < $1.order }
            await updatePageStage(with: .loaded(HomePageModel(sections: sortedSections)))
        } catch {
            await updatePageStage(with: .error(error.localizedDescription))
        }
    }

    func reloadData() {
        reloadTask?.cancel()
        reloadTask = Task { [weak self] in
            await self?.fetchHomeContent()
        }
    }
}

private extension HomeViewModel {
    @MainActor
    func updatePageStage(with state: PageState<HomePageModel>) {
        pageState = state
    }
}

Warning

Stored property '_pageState' of 'Sendable'-conforming class 'HomeViewModel' is mutable

As you can see the warning is quite clear but I cannot seem to find a way to make it disappear.

nonisolated is not supported on properties with property wrappers so that doesn't work.

Having a private(set) also.

As you can see the function updatePageStage is @MainActor bounded and my HomeViewModel is a @StateObject making it also @MainActor bounded through Swift implementation.

My question is simple: is there a way to remove this warning without using tricks like @unchecked Sendable or setting explicitly the entire class as @MainActor

Thank you in advance for any tips.

1

There are 1 answers

0
malhal On

When you use async/await you no longer need an object, it's simply:

struct HomeView {

    @State var results = []
    @State var error = "" 

    var body: some View {
        
        .task {
            do {
                results = try await fetchHomeContent()
            } catch {
                error = 
            }
        }
    }

The task is started when the underlying UIView appears and cancelled when it disappears. This simplifies async lifecycle management greatly. Also you can use task(id: pageNumber) to cancel and restart the task when the id param changes.