9

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

0

2 Answers 2

8

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) {
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Just missed the .constant as in @pratap answer
If it's binding, I didn't see what is ActivityIndicatorView, then it's better to have direct $model.loading.
3
import Combine

class SourcesListViewModel: ObservableObject {
    @Published var loading = true
    @Published var sources = [SourcesViewModel]()

    init() {
        fetchSources()
    }


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



import SwiftUI

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

    var body: some View {
        ActivityIndicatorView(isShowing: .constant(self.model.loading)) {
            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()
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.