Home > Net >  How to temporarily display a view before ContentView()?
How to temporarily display a view before ContentView()?

Time:01-15

I am building an app based on a large static database. Each time I open the app, I would like to check that its local database has been created from a JSON-encoded file. If this is not the case (i.e. the first time the app is opened), the data is decoded from the JSON-encoded file and inserted into the database.

This process takes about 10 seconds during which I would like to display a ProgressView() before the user has access to the interface (i.e. ContentView()).

EDIT - here is the solution:

class DatabaseBuilder: ObservableObject {
    $Published var isBuilding = true

    let database = SomeDatabase()

    private func decodeJSON() async throws {
        // long process to decode large JSON-encoded file
    }

    private func fillDatabase() async throws {
        // long process to add new model objects to database
    }

    public func build() async throws {
        if !database.isEmpty {
            isBuilding = false
        }

        try await decodeJSON()
        try await fillDatabase()

        isBuilding = false
    }
}
struct LargeDatabaseApp: App {
    @StateObject var builder = DatabaseBuilder()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .opacity(builder.isBuilding ? 0 : 1)
                .overlay { if builder.isBuilding { ProgressView() } }
                .task {
                    do {
                        try await builder.build()
                    } catch {
                        // handle error
                    }
                }
        }
    }

CodePudding user response:

you could try something like this:

import SwiftUI

@main
struct LargeDatabaseApp: App {
    @StateObject var builder = DatabaseBuilder()
    
    var body: some Scene {
        WindowGroup {
            if builder.isBuilding {
                ProgressView(){Label("building db", systemImage: "stopwatch")}
            } else {
                ContentView()
            }
            Spacer().frame(height: 0)
                .onAppear {
                    if builder.database.isEmpty {
                        do {
                            try builder.build()
                        }
                        catch {
                            print(error)
                        }
                    }
                }
        }
    }
}

class DatabaseBuilder: ObservableObject {
    @Published var isBuilding = false
    var database = ""
    
    public func build() throws {
        isBuilding = true
        // builds database...
        DispatchQueue.main.asyncAfter(deadline: .now()   5) {
            self.isBuilding = false
        }
    }
}

struct ContentView: View {
    var body: some View {
        Text("ContentView here")
    }
}

EDIT-1: you could also try using the scenePhase, like this:

@main
struct LargeDatabaseApp: App {
    @Environment(\.scenePhase) private var scenePhase
    
    @StateObject var builder = DatabaseBuilder()
    
    var body: some Scene {
        WindowGroup {
            if builder.isBuilding {
                ProgressView(){Label("building db", systemImage: "stopwatch")}
            } else {
                ContentView()
            }
        }
        .onChange(of: scenePhase) { phase in
            switch phase {
            case .background:
                print("time to write to your database")
                break
            case .active:
                print("setup the database")
                if builder.database.isEmpty {
                    do {
                        try builder.build()
                    }
                    catch {
                        print(error)
                    }
                }
                break
            case .inactive:
                print("inactive")
            default: break
            }
        }
    }
}
  •  Tags:  
  • Related