Calling a new view in a function SwiftUI

Viewed 58

I am currently using an api to grab the definitions for a specific word that the user has entered, and the api returns multiple definitions. I want the user to be able to choose what exact definition they want to pair a word with. Since I am interacting with an api, it is in a function and I cannot return anything out of it. I want to grab all the definitions and then show a new view where the user can pick the appropriate definition. How can I go about doing this? I've thought of making an ObservableObject that just has an array as a work around, but that seems a bit excessive. I am new to SwiftUI, so I am unsure whether or not this would be possible. However, I think it would not be because I am not trying to return a view anywhere or using any of the built in things that accepts views.

EDIT: I made SaveArray an ObservableObject and now my problem is that the object is not being updated by my getDef function call. Within the function it is but it is not editing the actual class or at least that is what it is looking like, because on my next view I have a foreach going through the array and nothing is displayed because it is empty. I am not sure whether that is because the sheet is being brought up before the getDef function is done executing.

struct AddWord: View {
    @ObservedObject var book: Book
    @ObservedObject var currentArray = SaveArray()
    @State var addingDefinition = false
    @State var word = ""
    @State var definition = ""
    @State var allDefinitions: [String] = []

    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        NavigationView {
            Form {
                TextField("Word: ", text: $word)
            }
            .navigationBarTitle("Add word")
            .navigationBarItems(trailing: Button("Add") {
                if self.word != "" {
                    book.words.append(self.word)
                
                    getDef(self.word, book, currentArray)
                    addingDefinition = true
                    self.presentationMode.wrappedValue.dismiss()
                }
            }).sheet(isPresented: $addingDefinition) {
                PickDefinition(definitions: currentArray, book: book, word: self.word)
            }

        }
    }
    func getDef(_ word: String, _ book: Book, _ definitionsArray: SaveArray) {
        let request = NSMutableURLRequest(url: NSURL(string: "https://wordsapiv1.p.rapidapi.com/words/\(word)")! as URL,
                                                cachePolicy: .useProtocolCachePolicy,
                                            timeoutInterval: 10.0)
        request.httpMethod = "GET"
        request.allHTTPHeaderFields = headers
        

        let session = URLSession.shared
        let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
            
            if (error != nil) {
                print(error)
            } else {
                do {
                    let dictionary = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String:Any]
                    //getting the dictionary
                    let dict = dictionary?["results"] as? [Any]
                    definitionsArray.currentArray = createArray((dict!))
                }
                catch {
                    print("Error parsing")
                }
            }
        })
    
        dataTask.resume()

    }
    func createArray(_ array: [Any]) -> [String] {
        //Get all the definitions given from the api and put it into a string array so you can display it for user to select the correct definiton for their context
        var definitions = [String]()
        for object in array {
            let dictDef = object as? [String: Any]
            definitions.append(dictDef?["definition"] as! String)
        }
        return definitions
    }
}

struct AddWord_Previews: PreviewProvider {
    static var previews: some View {
        AddWord(book: Book())
    }
}

struct PickDefinition: View {
    @ObservedObject var definitions: SaveArray
    var book: Book
    var word: String
    @Environment(\.presentationMode) var presentationMode
    var body: some View {
        NavigationView {
            VStack {
                ForEach(0 ..< definitions.currentArray.count) { index in
                    Button("\(self.definitions.currentArray[index])", action: {
                        print("hello")
                        DB_Manager().addWords(name: self.book.name, word: self.word, definition: self.definitions.currentArray[index])
                        book.definitions.append(self.definitions.currentArray[index])
                        self.presentationMode.wrappedValue.dismiss()
                    })
                }
            }
            .navigationTitle("Choose")
            
        }
    }

}


struct PickDefinition_Previews: PreviewProvider {
    static var previews: some View {
        PickDefinition(definitions: SaveArray(), book: Book(), word: "")
    }
}

1 Answers

If you can post more of your code, I can provide a fully working example (e.g. the sample JSON and the views/classes you have built). But for now, I am working with what you provided. I hope the below will help you see just how ObservableObject works.

@Published var dict = [String]() //If the api returns a list of strings, you can make this of type string - I do not have a sample of the JSON so I cannot be sure. If you can provide a sample of the JSON I can better define the way this should work.
    
    var body: some View {
        let session = URLSession.shared
        let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
            
            if (error != nil) {
                print(error)
            } else {
                do {
                    let dictionary = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String:Any]
                    //getting the dictionary
                    dict = dictionary?["results"] as? [Any] //note that here we are assigning the results of the api call to the Published variable, which our StateObject variable in ContentView is listening to!
                    var allDef = createArray((dict!))
                    
//No longer need to pass this data forward (as you have below) since we are publishing the information!

                    //Pass the array to the new view where the user will select the one they want
                    PickDefinition(definitions: allDef, book: self.book, word: self.word)
                }
                catch {
                    print("Error parsing")
                }
            }
        })
        
        dataTask.resume()
        
    }
}


struct ContentView: View {
    //StateObject receives updates sent by the Published variable
    @StateObject var dictArray = Api()
    
    var body: some View {
        NavigationView {
           
            List {
                ForEach(dictArray.dict.indices, id: \.self) { index in
                    Text(dictArray.dict[index])
                }
            }
        }
    }
}
Related