Search code examples
google-cloud-firestoreswiftuipicker

Storing a selected value from picker to use in other views - SwiftUI


I was very kindly helped to allow my picker to select a value from my Firestore database. What I would like to do is once that value is selected in my picker I would like to be able to show that value in other views. I have tried setting this up using UserDefaults but I'm not sure that's the way to go? If you could suggest a better method I'd be more than grateful. My code currently is below.

The value in the below code returns Unknown School each time but without the user defaults works flawlessly in fetching the data.

Thanks in advance.

import SwiftUI
import Firebase

struct SchoolDetailsView: View {
    @ObservedObject var schoolData = getSchoolData()
    @State private var selectedSchool = UserDefaults.standard.string(forKey: "") // `schoolName.id` is of type String

    var body: some View {
        VStack {
                Form {
                    Section {
                        Picker(selection: $selectedSchool, label: Text("School Name")) {
                            ForEach(schoolData.datas, id: \.id) {
                                Text($0.name)
                            }
                        }
                        Text("Selected School: \(selectedSchool ?? "Unknown School")")
                    }
                }.navigationBarTitle("School Selection")
            }
        }

struct SchoolPicker_Previews: PreviewProvider {
    static var previews: some View {
        SchoolDetailsView()
    }
}

class getSchoolData : ObservableObject{
    
    @Published var datas = [schoolName]()
    
    init() {
        
        let db = Firestore.firestore()
        
        db.collection("School Name").addSnapshotListener { (snap, err) in
            
            if err != nil{
                
                print((err?.localizedDescription)!)
                return
            }
            
            for i in snap!.documentChanges{
                
                let id = i.document.documentID
                let name = i.document.get("Name") as! String
                
                self.datas.append(schoolName(id: id, name: name))
            }
        }
    }
}

struct schoolName : Identifiable {
    
    var id : String
    var name : String
}
}

Solution

  • First, the UserDefaults key for your variable can't be empty:

    @State private var selectedSchool: String = UserDefaults.standard.string(forKey: "selectedSchool") ?? "Unknown School"
    

    Then you can use onReceive to update the variable:

    .onReceive(Just(selectedSchool)) {
        UserDefaults.standard.set($0, forKey: "selectedSchool")
    }
    

    Full code:

    import Combine
    import Firebase
    import SwiftUI
    
    struct SchoolDetailsView: View {
        @ObservedObject var schoolData = getSchoolData()
        @State private var selectedSchool = UserDefaults.standard.string(forKey: "selectedSchool")
    
        var body: some View {
            VStack {
                Form {
                    Section {
                        Picker(selection: $selectedSchool, label: Text("School Name")) {
                            ForEach(schoolData.datas, id: \.id) {
                                Text($0.name)
                            }
                        }
                        .onReceive(Just(selectedSchool)) {
                            UserDefaults.standard.set($0, forKey: "selectedSchool")
                        }
                        Text("Selected School: \(selectedSchool)")
                    }
                }.navigationBarTitle("School Selection")
            }
        }
    }
    

    Note that in SwiftUI 2 / iOS 14 you can use @AppStorage instead.