Search code examples
swiftswiftuiobservedobject

SwiftUI - Why won't my @Published variable update within my @ObservedObject call?


In essence, I'm learning by creating a simple app that takes an ISBN and returns the book information. I've created a view file to display this information along with a text entry for capturing the ISBN. The logic side of things as pertaining to getting the information from the internet is fine, however, when I try to update the view with the modified variables, it only prints them as declared, and NOT redrawing them after modification. Any advice would be appreciated. Thanks!

This specific example references @Published var test = "Testing 1 2 3 as the default, and after the search, the var is modified to test = modified text which is then printed in the text view of TestView.swift. The print statement correctly displays the modified text, however it does not update in the view in which it is passed. The goal is to pass the final dictionary, but for testing purposes I'm using a simple string variable.

TestFile.swift

import Foundation
import SwiftyXMLParser
import SwiftUI

class getdata: ObservableObject {
    
    @Published var outputDict: [String:String] = [:]
    @Published var test = "Testing 1 2 3"

    .
    .

    //code to grab xml from url

    .
    .

    func parseData(input: String) {
        var title: String?
        var author: String?
        let xml = try! XML.parse(input)
        if let text = xml["GoodreadsResponse", "book", "title"].text {
            title = text
            outputDict["title"] = title
        }
        if let text = xml["GoodreadsResponse", "book", "authors", "author", "name"].text {
            author = text
            outputDict["author"] = author
        }
        print("Title: \(outputDict["title"]!), Author: \(outputDict["author"]!)")
        test = "modified text"
        print(test)
    }
}

TestView.swift

import SwiftUI

struct testView: View {
    
    @State var textEntry: String = ""
    @ObservedObject var viewModel: getdata
    
    var body: some View {
        let try1 = getdata()
        VStack {
            TextField("Enter ISBN", text: $textEntry)
                .padding()
            Button(action: {try1.getData(bookID: textEntry)}, label: {
                Text("Search")
            })
            Text("Title: \(self.viewModel.test)")
        }
    }
}
    

struct testView_Previews: PreviewProvider {
    static var previews: some View {
        testView(viewModel: getdata())
    }
}

Solution

  • Although I don't think your example compiles, and you haven't provided the definitions of some of the functions, I can point you at one error:

    Inside the body of the view you are creating a new Observable object:

    let try1 = getdata()
    

    And your Button calls messages on this:

    Button(action: {try1.getData(bookID: textEntry)}...
    

    But your TextView reads from the original viewModel:

    Text("Title: \(self.viewModel.test)")
    

    You are interacting with try1, but deriving your view from viewModel. They are separate objects.