Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Displaying activity indicator while loading async request in SwiftUI

I have a basic view that displays a list that fetches data from an API. I want to implement an activity indicator while the data is being retrieved from the API. In MVC we could use a delegate and protocol and make the view controller inherit the protocol, and after the model has finished fetching the data we call the delegate to tell the view controller that the data has finished retrieving (now hide the activity indicator, etc.). How to achieve a similar thing in SwiftUI and it's MVVM style?

I have tried implementing an activity indicator from this question, I just don't get how and when to stop it: Activity indicator in SwiftUI

My SourcesViewModel (it fetches news article sources from newsapi.org)

import UIKit

class SourcesViewModel: Identifiable {

    let id = UUID()

    let source: Sources

    init(source: Sources) {
        self.source = source
    }

    var name: String {
        return self.source.sourceName
    }

    var description: String {
        return self.source.sourceDescription
    }
}

My SourcesListViewModel:

import Combine

class SourcesListViewModel: ObservableObject {

    init() {
        fetchSources()
    }

    @Published var sources = [SourcesViewModel]()
    private func fetchSources() {
        NetworkManager.shared.getSourceData { (sources) in
            self.sources = sources.map(SourcesViewModel.init)
        }
    }
}

Lastly, my SourcesView:

import SwiftUI

struct SourcesView: View {
    @ObservedObject var model = SourcesListViewModel()

    var body: some View {
        ActivityIndicatorView(isShowing: .constant(true)) {
            NavigationView {
                List(self.model.sources) { source in
                    VStack(alignment: .center) {
                        Text(source.name)

                        Text(source.description)
                            .foregroundColor(.secondary)
                            .lineLimit(3)
                    }
                    .navigationBarTitle(Text("Sources"), displayMode: .inline)
                }
            }
        }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        SourcesView()
    }
}

The result:

how to stop loading

like image 320
OldTimes Avatar asked Nov 20 '19 14:11

OldTimes


1 Answers

Your view model should have loading state, like the following

@Published var loading = false
private func fetchSources() {
    self.loading = true
    NetworkManager.shared.getSourceData { (sources) in
        self.sources = sources.map(SourcesViewModel.init)
        self.loading = false
    }
}

and activity indicator should be bound to it, like

ActivityIndicatorView(isShowing: $model.loading) {
like image 53
Asperi Avatar answered Dec 11 '22 08:12

Asperi