Reputation: 360
My problem is that when I change my observed objects string property to another value, it updates the JSON
Image values printing out the updated values(Using the Unsplash API), but the WebImage (from SDWebImageSwiftUI
) doesn't change.
The struct that Result applies to:
struct Results : Codable {
var total : Int
var results : [Result]
}
struct Result : Codable {
var id : String
var description : String?
var urls : URLs
}
struct URLs : Codable {
var small : String
}
Here is the view which includes the webImage thats supposed to update:
struct MoodboardView: View {
@ObservedObject var searchObjectController = SearchObjectController.shared
var body: some View {
List {
VStack {
Text("Mood Board: \(searchObjectController.searchText)")
.fontWeight(.bold)
.padding(6)
ForEach(searchObjectController.results, id: \.id, content: { result in
Text(result.description ?? "Empty")
WebImage(url: URL(string: result.urls.small) )
.resizable()
.frame(height:300)
})
}.onAppear() {
searchObjectController.search()
}
}
}
}
Here is the class which does the API Request:
class SearchObjectController : ObservableObject {
static let shared = SearchObjectController()
private init() {}
var token = "gQR-YsX0OpwkYpbjhPVi3b4kSR-DtWrR5phwDm2kPMM"
@Published var results = [Result]()
@Published var searchText : String = "forest"
func search () {
let url = URL(string: "https://api.unsplash.com/search/photos?query=\(searchText)")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("Client-ID \(token)", forHTTPHeaderField: "Authorization")
print("request: \(request)")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else {return}
print(String(data: data, encoding: .utf8)!)
do {
let res = try JSONDecoder().decode(Results.self, from: data)
DispatchQueue.main.async {
self.results.append(contentsOf: res.results)
}
//print(self.results)
} catch {
print("catch: \(error)")
}
}
task.resume()
}
}
Here is how I change the value of the searchText in a Button, if you would like to see:
struct GenerateView: View {
@ObservedObject var searchObjectController = SearchObjectController.shared
@State private var celsius: Double = 0
var body: some View {
ZStack {
Color.purple
.ignoresSafeArea()
VStack{
Text("Generate a Random Idea")
.padding()
.foregroundColor(.white)
.font(.largeTitle)
.frame(maxWidth: .infinity, alignment: .center)
Image("placeholder")
Slider(value: $celsius, in: -100...100)
.padding()
Button("Generate") {
print("topic changed to\(searchObjectController.searchText)")
searchObjectController.searchText.self = "tables"
}
Spacer()
}
}
}
}
Upvotes: 0
Views: 723
Reputation: 1870
It turns out you update the search text but don't search again. See this does the trick:
Button("Generate") {
print("topic changed to\(searchObjectController.searchText)")
searchObjectController.searchText.self = "tables" // you changed the search text but didnt search again
self.searchObjectController.search() // adding this does the trick
}
Also. I updated your code to use an EnvironmentObject
. If you use one instance of an object throughout your app. Consider making it an EnvironmentObject
to not have to pass it around all the time.
Adding it is easy. Add it to your @main
Scene
import SwiftUI
@main
struct StackoverflowApp: App {
@ObservedObject var searchObjectController = SearchObjectController()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(self.searchObjectController)
}
}
}
And using it even simpler:
struct MoodboardView: View {
// Env Obj. so we reference only one object
@EnvironmentObject var searchObjectController: SearchObjectController
var body: some View {
Text("")
}
}
Here is your code with the changes and working as expected: I added comments to the changes I made
struct ContentView: View {
var body: some View {
MoodboardView()
}
}
struct GenerateView: View {
@EnvironmentObject var searchObjectController: SearchObjectController
@State private var celsius: Double = 0
var body: some View {
ZStack {
Color.purple
.ignoresSafeArea()
VStack{
Text("Generate a Random Idea")
.padding()
.foregroundColor(.white)
.font(.largeTitle)
.frame(maxWidth: .infinity, alignment: .center)
Image("placeholder")
Slider(value: $celsius, in: -100...100)
.padding()
Button("Generate") {
print("topic changed to\(searchObjectController.searchText)")
searchObjectController.searchText.self = "tables" // you changed the search text but didnt search again
self.searchObjectController.search() // adding this does the trick
}
Spacer()
}
}
}
}
class SearchObjectController : ObservableObject {
//static let shared = SearchObjectController() // Delete this. We want one Object of this class in the entire app.
//private init() {} // Delete this. Empty Init is not needed
var token = "gQR-YsX0OpwkYpbjhPVi3b4kSR-DtWrR5phwDm2kPMM"
@Published var results = [Result]()
@Published var searchText : String = "forest"
func search () {
let url = URL(string: "https://api.unsplash.com/search/photos?query=\(searchText)")
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("Client-ID \(token)", forHTTPHeaderField: "Authorization")
print("request: \(request)")
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else {return}
print(String(data: data, encoding: .utf8)!)
do {
let res = try JSONDecoder().decode(Results.self, from: data)
DispatchQueue.main.async {
self.results.append(contentsOf: res.results)
}
//print(self.results)
} catch {
print("catch: \(error)")
}
}
task.resume()
}
}
struct MoodboardView: View {
// Env Obj. so we reference only one object
@EnvironmentObject var searchObjectController: SearchObjectController
var body: some View {
List {
VStack {
Text("Mood Board: \(searchObjectController.searchText)")
.fontWeight(.bold)
.padding(6)
ForEach(searchObjectController.results, id: \.id, content: { result in
Text(result.description ?? "Empty")
WebImage(url: URL(string: result.urls.small) )
.resizable()
.frame(height:300)
})
}.onAppear() {
searchObjectController.search()
}
// I added your update button here so I can use it.
GenerateView()
}
}
}
struct Results : Codable {
var total : Int
var results : [Result]
}
struct Result : Codable {
var id : String
var description : String?
var urls : URLs
}
struct URLs : Codable {
var small : String
}
Ps. please not that it appears when you search you just append the results to the array and don't delete the old ones. Thats the reason why you still see the first images after updating. The new ones just get appended at the bottom. Scroll down to see them. If you don't want that just empty the array with results upon search
Upvotes: 1