Search code examples
viewbindingswiftuiuidocumentpickervc

Change State with DocumentPicker SwiftUI


I'm trying to get a List View to appear after selecting a document with documentPicker. Getting the following error...

Fatal error: No ObservableObject of type Switcher found.
A View.environmentObject(_:) for Switcher may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-30.4/Core/EnvironmentObject.swift, line 55

It seems like I should use an EnviromentObject binding to have all views be able to read, access and update the Switcher class. Under the Coordinator Class in CSVDocumentPicker.swift is where things seem to go wrong.
I'm using @EnvironmentObject var switcher:Switcher and using the documentPicker function to toggle the switcher state so the Lists View will be displayed. I'm stumped.

SceneDelegate.swift

import UIKit
import SwiftUI


class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var switcher = Switcher()

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let contentView = ContentView().environmentObject(Switcher())

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView.environmentObject(switcher))


            self.window = window
            window.makeKeyAndVisible()
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) {

    }

    func sceneDidBecomeActive(_ scene: UIScene) {

    }

    func sceneWillResignActive(_ scene: UIScene) {

    }

    func sceneWillEnterForeground(_ scene: UIScene) {

    }

    func sceneDidEnterBackground(_ scene: UIScene) {

    }


}

CSVDocumentPicker.swift

import Combine
import SwiftUI

class Switcher: ObservableObject {
var didChange = PassthroughSubject<Bool, Never>()
  var isEnabled = false {
      didSet {
          didChange.send(self.isEnabled)
      }
  }
}


struct CSVDocumentPicker: View {
  @EnvironmentObject var switcher:Switcher
    @State private var isPresented = false

    var body: some View {
        VStack{
            Text("csvSearch")
                Button(action: {self.isPresented = true
                })
                {Text("import")
                Image(systemName: "folder").scaledToFit()
                }.sheet(isPresented: $isPresented) {
                    () -> DocumentPickerViewController in
                    DocumentPickerViewController.init(onDismiss: {
                    self.isPresented = false
                    })
            }
            if switcher.isEnabled  {
                        ListView()
                     }

        }
    }
}

struct CSVDocumentPicker_Previews: PreviewProvider {
    static var previews: some View {
        CSVDocumentPicker().environmentObject(Switcher())
    }
}
/// Wrapper around the `UIDocumentPickerViewController`.
struct DocumentPickerViewController {

    private let supportedTypes: [String] = ["public.item"]

    // Callback to be executed when users close the document picker.
    private let onDismiss: () -> Void

    init(onDismiss: @escaping () -> Void) {
        self.onDismiss = onDismiss
    }
}

// MARK: - UIViewControllerRepresentable

extension DocumentPickerViewController: UIViewControllerRepresentable {

    typealias UIViewControllerType = UIDocumentPickerViewController

    func makeUIViewController(context: Context) -> DocumentPickerViewController.UIViewControllerType {
        let documentPickerController = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
        documentPickerController.allowsMultipleSelection = false
        documentPickerController.delegate = context.coordinator
        return documentPickerController
    }

    func updateUIViewController(_ uiViewController: DocumentPickerViewController.UIViewControllerType, context: Context) {}

    // MARK: Coordinator

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UIDocumentPickerDelegate, ObservableObject {
        @EnvironmentObject var switcher:Switcher
        var parent: DocumentPickerViewController

        init(_ documentPickerController: DocumentPickerViewController) {
            parent = documentPickerController
        }

        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {

            globalPathToCsv = url
            loadCSV()

         switcher.isEnabled.toggle()
          }

        func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
            parent.onDismiss()

        }
    }
}

ContentView.swift

import SwiftUI
import UIKit

var globalPathToCsv:URL!
var csvArray = [[String:String]]()
var csv = CSVaccessability()


func loadCSV(){
    csv.csvToList()
  // print(csvArray)

}
struct ContentView: View {
 @EnvironmentObject var switcher:Switcher

      var body: some View {
        VStack{
            CSVDocumentPicker().environmentObject(Switcher())

                                    }
                            }
                    }

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(Switcher())
    }
}

ListView.swift

import SwiftUI

struct ListView: View {
    var body: some View {
        HStack{
        List {
            ForEach(csvArray, id:\.self) { dict in Section {DataList(dict: dict)} }
        }
        }}
}

struct DataList : View {
  @State var dict = [String: String]()
    var body: some View {
        let keys = dict.map{$0.key}
        let values = dict.map {$0.value}

        return  ForEach(keys.indices) {index in
            HStack {
                Text(keys[index])
                Text("\(values[index])")
            }
        }
    }
}

struct ListView_Previews: PreviewProvider {
    static var previews: some View {
        ListView()
    }
}

CSVaccessability.swift

import Foundation
import SwiftCSV

var csvData:[[String]]!
var headers:[String] = []


class CSVaccessability {

    var numberOfColumns:Int!
    var masterList = [[String:Any]]()


    func csvToList(){
        if let url = globalPathToCsv {
                    do {
                        print(url)
                        let csvFile: CSV = try CSV(url: globalPathToCsv)
                        let csv = csvFile
                        //print(stream)
                        //print(csvFile)
                        headers = csv.header
                        csvArray=csv.namedRows
                            } catch {print("contents could not be loaded")}}
                               else {print("the URL was bad!")}

    }
}

I've imported SwiftCSV for this project...


Solution

  • Created a new class... ToggleView.swift

    import Foundation
    class ToggleView: ObservableObject {
        @Published var toggleView: Bool = false
    
    }
    

    Added the Environment Object to ContentView.swift @EnvironmentObject var viewToggle: ToggleView

    Also added .environmentObject(ToggleView()) to any view that would be called and cause a crash the crash logs helped with this...

              Text("csvSearch")
                               Button(action: {self.isPresented = true
                                self.viewToggle.toggleView.toggle()
                               // self.switcher = true
                               })
                               {Text("import")
                               Image(systemName: "folder").scaledToFit()
                               }.sheet(isPresented: $isPresented) {
                                   () -> DocumentPickerViewController in
                                DocumentPickerViewController.init()
                                                      }
                if self.picker {
                    DocumentPickerViewController().environmentObject(ToggleView())
                }
    
                if self.viewToggle.toggleView{
                    ListView()
                            }
                       }
                   }
                }