I'm trying to fetch urls from an API based on a search query. If I hardcode the query parameter to some value in the url (i.e. "fitness"), I get a response.
If I set the query parameter to an interpolated value to be inserted at a later date, the app has no images at runtime-- which makes sense.
However, when I enter a search query into my search bar, I cannot fetch the results, either. In fact, my results are 0.
Here's the error:
po jsonResult
▿ APIResponse
- total : 0
- results : 0 elements
Here's my code:
Models
import Foundation
struct APIResponse: Codable {
let total: Int
let results: [Result]
}
struct Result: Codable {
let id: String
let urls: URLS
}
struct URLS: Codable {
let full: String
}
View
import SwiftUI
struct SimpleView: View {
@ObservedObject var simpleViewModel = SimpleViewModel.shared
@State private var searchText = ""
@State private var selected: String? = nil
var filteredResults: [Result] {
if searchText.isEmpty {
return simpleViewModel.results
} else {
return simpleViewModel.results.filter { $0.urls.full.contains(searchText) }
}
}
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 0) {
ForEach(filteredResults, id: \.id) { result in
NavigationLink(destination: SimpleDetailView()) {
VStack {
AsyncImage(url: URL(string: result.urls.full)) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.scaledToFill()
.frame(maxWidth: .infinity)
.onTapGesture {
withAnimation(.spring()) {
if self.selected == result.urls.full {
self.selected = nil
} else {
self.selected = result.urls.full
}
}
hideKeyboard()
}
.scaleEffect(self.selected == result.urls.full ? 3.0 : 1.0)
}
}
}
}
}
.onAppear {
simpleViewModel.fetchPhotos(query: searchText)
}
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
}
}
}
struct SimpleView_Previews: PreviewProvider {
static var previews: some View {
SimpleView()
}
}
ViewModel
import Foundation
class SimpleViewModel: ObservableObject {
static let shared = SimpleViewModel()
private init() {}
@Published var results = [Result]()
func fetchPhotos(query: String) {
let url = "https://api.unsplash.com/search/photos?page=1&query=\(query)&client_id=blahblahblahblahblahblahblahblah"
guard let url = URL(string: url) else { return }
let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let data = data, error == nil else { return }
do {
let jsonResult = try JSONDecoder().decode(APIResponse.self, from: data)
DispatchQueue.main.async {
self?.results = jsonResult.results
}
} catch {
print("Error: \(error)")
}
}
task.resume()
}
}
How can I search for images in my SimpleView based on my search query in my SimpleViewModel?
- Setting breakpoints (how I discovered 0 values)
- Ternary operators to check for search values or not
- Setting my computer on fire
UPDATE
I added this to the code as @workingdog suggested, but with an else
statement.
.onSubmit(of: .search) {
if searchText.isEmpty {
simpleViewModel.results = filteredResults
} else if !searchText.isEmpty {
simpleViewModel.fetchPhotos(query: searchText)
}
}
Here's what happens:
- Images are fetched, but not displayed in view
- Search query on submit renders nothing
- Pressing cancel enacts the query
- The images are displayed
- Images remain and are displayed. Go back to 2.
CodePudding user response:
In your SimpleView
, the .onAppear { simpleViewModel.fetchPhotos(query: searchText }
is
called only when the view appears, and uses searchText = ""
. In other words you have an empty query. So remove the .onAppear{...}
, it does nothing.
Add something like this, to fetch the photos when the searchText
is submitted.
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
.onSubmit(of: .search) {
if !searchText.isEmpty {
simpleViewModel.fetchPhotos(query: searchText)
}
}