Hi there,
I am trying to make an app working with Dogs API. So far, I have been able to get the get request from the API, where I am getting all the dogs and displaying them in the ListView in my App. The second thing I am aiming at is to be able to click on the dog from the list, which brings you in to DogDetailView. In this view I would like to display pictures only of the selected dog. Here’s where my struggle is.
Let me walk you through my thoughts and how I planned to do it.
- I have made 2 Models one for Dog and one for Breed. I am not going to show here the Dog class because it is irrelevant, so here is a Breed struct, where I am only basically re-writing JSON.
struct Breed: Decodable, Identifiable {
var id:UUID?
var message: [String]?
var status: String
}
Here’s also the JSON ( part of it ). Basically, it just consists of message array of all images of that specific breed, in that case it is boxer and status string.
{
"message": [
"https://images.dog.ceo/breeds/boxer/28082007167-min.jpg",
"https://images.dog.ceo/breeds/boxer/IMG_0002.jpg",
"https://images.dog.ceo/breeds/boxer/IMG_3394.jpg",
"https://images.dog.ceo/breeds/boxer/n02108089_1.jpg"
],
"status": "success"
}
- Then I have ContentModel, where I am parsing, decoding the JSON and assigning it to my variables. See below.
class ContentModel: ObservableObject {
@Published var dogs = [Dog]()
@Published var breeds = [Breed]()
init() {
getDogs()
getBreeds()
}
func getDogs(){
// Create URL
let urlString = Constants.apiUrl
let url = URL(string: urlString)
if let url = url {
// Create URL request
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
request.httpMethod = "GET"
// Get URLSession
let session = URLSession.shared
// Create Data Task
let dataTask = session.dataTask(with: request) { (data, response, error) in
// Check that there is not an error
if error == nil {
do {
// Parse JSON
let decoder = JSONDecoder()
let result = try decoder.decode(Response.self, from: data!)
print(result)
// Assign result to the dogs property
DispatchQueue.main.async {
self.dogs = result.dogs
}
} catch {
print(error)
}
}
}
// Start the Data Task
dataTask.resume()
}
}
func getBreeds() {
// Create URL
let urlString = Constants.breedsUrl
let url = URL(string: urlString)
if let url = url {
// Create URL request
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
request.httpMethod = "GET"
// Get URLSession
let session = URLSession.shared
// Create Data Task
let dataTask = session.dataTask(with: request) { (data, response, error) in
// Check that there is not an error
if error == nil {
do {
// Parse JSON
let decoder = JSONDecoder()
let resultBreed = try decoder.decode(Breed.self, from: data!)
print(resultBreed)
// Assign result to the dogs property
DispatchQueue.main.async {
self.breeds = [resultBreed]
}
} catch {
print(error)
}
}
}
// Start the Data Task
dataTask.resume()
}
}
}
As you can see methods getDogs() and getBreeds() are identical, because I wanted to have a same approach and see if the one works then the other has too. At least that was my vision, haha.
By the way this is breedsUrl = https://dog.ceo/api/breed/boxer/images
Anyways, in the step 3. I am creating a detail view for a Dog. Where I want to display his name and pictures of that breed. This is how I am trying to do it.
struct DogDetailView: View {
@EnvironmentObject var model: ContentModel
var dog:Dog
var body: some View {
VStack {
List(0..<model.breeds.count, id: \.self) { index in
ForEach(model.breeds[index].message!, id: \.self) { m in
AsyncImage(url: URL(string: "\(m)")) { phase in
switch phase {
case .empty:
Color.purple.opacity(0.1)
case .success(let image):
image
.resizable()
.scaledToFit()
case .failure(_):
Image(systemName: "exclamationmark.icloud")
.resizable()
.scaledToFit()
@unknown default:
Image(systemName: "exclamationmark.icloud")
}
}
.frame(width:300, height: 300)
.cornerRadius(20)
}
}
}
.navigationBarTitle(dog.name)
.padding(.top, 0)
}
}
struct DogDetailView_Previews: PreviewProvider {
static var previews: some View {
let model = ContentModel()
DogDetailView(dog: model.dogs[0])
}
}
This way I am getting the images of the one breed of the dog, but it is ONLY of the one that is specified in the breedsUrl. How can I substitute this value in the URL itself? So let’s say in this case, I would like to have in that class to like like this: https://dog.ceo/api/breed/\(dog.name)/images
.
Any idea how can I do this? Or is there any other way how can should I approach this problem?
Thanks in advance!
Cheers