Closed ca13ra1 closed 3 years ago
I think I need to reimplement both RequestView
and RequestImage
, neither one has a very good implementation, and I don't see anything wrong with your code immediately.
You should still be able to use Request
in your manual load
function using .call()
or as a publisher in the meantime.
Ok, I have it working in this PR: https://github.com/carson-katri/swift-request/pull/46
Please try out the swiftui
branch and lmk if it works properly now. You may want to refactor to use the new initializers for RequestView
, as I deprecated the old ones. For the refresh to work properly you will need to make sure your app only supports iOS 14+/macOS 11+. You should also not need that refresh
query param trick. That is just to refresh when there is no other change, but it looks like you are actually searching for something different, so the change will still trigger.
@carson-katri Thanks for the quick fix! I'm testing now but can't seem to figure out the correct syntax. I'm getting Failed to produce diagnostic for expression; please file a bug report
struct NYTimes: Codable {
let copyright: String?
let response: Response
}
struct Response: Codable {
let docs: [Doc]
}
struct Doc: Codable {
let lead_paragraph: String?
}
struct ContentView: View {
var body: some View {
RequestView(NYTimes.self, Request {
Url("https://api.nytimes.com/svc/search/v2/articlesearch.json")
Query(["api-key": "API_KEY"])
}) { news in
switch news {
case .loading: ProgressView()
case let .failure(error): Text(error.localizedDescription)
case let .success(news):
List(news?.response.docs ?? [], id: \.lead_paragraph) { article in
Text(article.lead_paragraph ?? "")
}
}
}
}
}
I think your body
should be:
RequestView(AnyRequest<NYTimes> {
Url("https://api.nytimes.com/svc/search/v2/articlesearch.json")
Query(["api-key": "API_KEY"])
}) { res in
switch res {
case .loading: ProgressView()
case let .failure(error): Text(error.localizedDescription)
case let .success(news):
List(news.response.docs, id: \.lead_paragraph) { article in
Text(article.lead_paragraph ?? "")
}
}
}
The result is no longer Optional, since there are now loading
and failure
cases, and you pass an AnyRequest
instead of the type and Request
separately. Let me know if that works for you.
@carson-katri It does in fact work but I'm seeing incorrect data in my list. I added a enum sort for newest, oldest, relevance alongside my search but seems to load incorrectly. Also the API can't call too many times so when typing it refreshes every time I type so it exhausting it's resources quickly.
I would suggest adding a debounce on your text field. Then it will wait for you to stop typing before updating the state. There are probably some examples on StackOverflow somewhere. One way would be to create an ObservableObject
that contains the raw TextField
input and a debounced publisher. I use something similar to this in my apps:
final class Debounce<Value: Equatable>: ObservableObject {
@Published var rawValue: Value
@Published var debounced: Value
init(initialValue: Value) {
self.rawValue = initialValue
self.debounced = initialValue
self.$rawValue
.removeDuplicates()
.debounce(for: 2, scheduler: DispatchQueue.main)
.assign(to: &debounced)
}
}
struct SomeView: View {
@ObservedObject var debouncedInput = Debounce<String>(initialValue: "")
var body: some View {
TextField(value: $debouncedInput.rawValue)
Text("The value debounced is: \(debouncedInput.debounced)")
}
}
Now it will wait for 2 seconds until you stop typing before updating the View.
@carson-katri I really appreciate your help. I was able to solve without RequestView. Only a month into learning Swift but I'm going to keep trying RequestView since it's an unbelievably awesome swift package! 😄
Thanks! And RequestView
is certainly not a one size fits all solution. However, if you wanted you could replace the URLSession call with a manual call on your Request
in load
:
AnyRequest<Article> {
Url("")
}
.onObject { article in
// use the article
}
.call()
I will try that! Thanks once again, you’ve really helped me out 👌
No problem! I'm going to leave this issue open until the PR lands on main.
If I select a NavigationLink
in my List
and go back to my List
it starts out at the very top of my list, no matter what item I select. The previous version worked just fine, I was able to go back to whatever item was selected.
struct ContentView: View {
var body: some View {
/// Timestamp for Marvel API
let ts = String(Date().timeIntervalSince1970)
/// MD5 hash for Marvel API
let hash = MD5(string:"\(ts)\("PRIVATE_KEY")\("PUBLIC_KEY")")
NavigationView {
RequestView(AnyRequest<Marvel> {
Url("https://gateway.marvel.com:443/v1/public/characters")
/// API is limited to 100 per call
/// How to call more using RequestView?
Query(["limit": "100", "apikey": "API_KEY", "ts": ts, "hash": hash])
Method(.get)
Header.Accept(.json)
Header.UserAgent(.firefoxMac)
/// Error handling
}) { data in
switch data {
case .loading: ProgressView()
case let .failure(error): Text(error.localizedDescription)
case let .success(marvel):
List(marvel.data.results, id: \.name) { character in
/// Push MarvelCharacterDetailView to show character information
NavigationLink(destination: CharacterDetail(url: "\(character.thumbnail.path).\(character.thumbnail.thumbnailExtension)", text: character.description ?? "", title: character.name ?? "", copyright: marvel.copyright ?? "")) {
HStack {
/// Load image using RequestImage using path + extension from character information
RequestImage(Url("\(character.thumbnail.path).\(character.thumbnail.thumbnailExtension)"), animation: nil)
.aspectRatio(contentMode: .fill)
.frame(width: 30, height: 30, alignment: .center)
.clipped()
.cornerRadius(5.0)
Text(character.name ?? "")
}
}
}
.listStyle(PlainListStyle())
.navigationBarTitle(Text(""), displayMode: .inline)
}
}
}
}
}
I think I know what's causing this. Let me see what I can do.
Thank you, again! 😊
Can you try the latest commit on the SwiftUI
branch? I pushed a possible fix.
It's been fixed in the latest commit!
I'm using
RequestView
to fetch data from a News API. I attempted to use your example of adding a Query string and update the number incrementally like in your example but it didn't actually refresh the view.My query string is
Query(["q": text, "apiKey": "API_KEY"])
where text is my variable for aTextField
string. I need to refresh the RequestView when my key changes. I've been unable to do so using RequestView but was able to usingURLRequest
manually and calling afunc load()
on my TextField'sonCommit
function. I'd really like to keep using RequestView in my project because it's very useful and easy to use for a beginner like myself! 😄RequestView code not updating here Working code without RequestView here