UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Showing conditional progress indicator

Forums > SwiftUI

I was just starting to learn Swift when SwiftUI came out, and I've spent most of the last year working on the backend of my app and not touching the interface elements. So this is really my first time working in SwiftUI aside from doing some of the "my first app" type tutorials, which didn't leave me quite prepared to figure out how to accomplish what I'm trying to do, especially since I'm also incorporating a couple other concepts I'm not terribly familiar with yet.

I'm building an app for audiobook library management. My primary view should be the list of books. But if the list of books is empty, it should be a view prompting the user to import some files. That part I've pretty much got worked out, except for one question.

struct FileImportView: View {
    @State var showImporter: Bool = false
    @State var importProgress: Progress

    var body: some View {
        Button(...)
        .fileImporter(isPresented: $showImporter,
                      allowedContentTypes: [ ... ],
                      allowsMultipleSelection: true,
                      onCompletion: { result in
                        if let urls = try? result.get() {
                            importProgress = ImportManager(urls).progress
                        }
                      })
    }
}

I'm still struggling with when to use @Binding and when to use @State, and this is my first time using Progress as well. If I want this view to set the importProgress to my other views, I would use @State here, correct? And the receiving view would use @Binding? Or have I gotten those mixed up (again)? Is it even appropriate to use that here, or should I be using another kind of variable?

Sorry, that ended up being more than one question.

Moving on.

In my ContentView, once a book is added to the database, I want to show the list of books, and for the list to update each time a new book is added. If an import is in progress still, I want a progress indicator, but I'd like it to be somewhere relatively inconspicuous. A corner of a bottom or top bar would be my preference, but I'm running into the issue of NavigationView not looking right in larger display devices. (This app is going to be targetted at iPad and MacOS users, because let's face it, few people are going to be curating their whole library on a phone.) So I'm not sure I can actually put anything in the navigation bar, which leaves me with putting the ProgressView in a ZStack and tucking it in a corner somewhere, overlaying the BookListView

But ZStack and GeometryReader are another new concept I'm struggling to get the hang of, especially since I'm not sure I'm doing the rest of it right in the first place.

struct ContentView: View {

    @EnvironmentObject var library: LibraryObject
    @Binding var importProgress: Progress

    var body: some View {
        if library.database.books.isEmpty {
            FileImportView(importProgress: importProgress)
        } else {
            ZStack {
                if importProgress.fractionCompleted < 100 {
                    ProgressView("Import in Progress...", value: importProgress.fractionCompleted, total: 100)
                    BookListView(library: _library)
                } else {
                    BookListView(library: _library)
                }
            }
        }
    }
}

My final question: I'm not sure I understand what to do with this:

@main
struct AudiobookLibrarianApp: App {
    var database = LibraryObject(database: LibraryCache.default.database)

    var body: some Scene {
        WindowGroup {
            ContentView(importProgress: $importProgress).environmentObject(database)
        }
    }
}

I'm not sure how I should capture the importProgress variable for use here. SwiftUI doesn't seem to want me to use @State and I'm not sure what else I should be using.

Thank you.

2      

I really really really suggest you break this into distinct forum topics to ask a single question at a time.... in relation to property wrappers:

Paul specifies all of the property wrappers and what they do/how they work here: https://www.hackingwithswift.com/quick-start/swiftui/all-swiftui-property-wrappers-explained-and-compared

  • @Binding refers to value type data owned by a different view. Changing the binding locally changes the remote data too. This does not own its data.
  • @ObservedObject refers to an instance of an external class that conforms to the ObservableObject protocol. This does not own its data.
  • @State lets us manipulate small amounts of value type data locally to a view. This owns its data.
  • @StateObject is used to store new instances of reference type data that conforms to the ObservableObject protocol. This owns its data.

2      

Thank you for replying. I apologize that I didn't realize until today that you had (I haven't yet found notification preferences.)

I'm coming to realize that my data model may be a little too complex for the "out of the box" options in SwiftUI, which is why I'm having trouble applying the "out of the box" solutions most tutorials cover. But I will take your advice and break those up into more case-by-case type questions. Thank you.

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.