Question
When I receive URL strings nested in JSON from the request response, what would be the best practices for downloading data from them?
Detail
For example, let's say I have downloaded several profile data from a network and then need to display them in a table where the names are displayed with profile images.
Raw data
{
{
"name": "Jack",
"imageUrl": "https://example.image/1.jpg"
},
{
"name": "Tom",
"imageUrl": "https://example.image/2.jpg"
},
{
"name": "Mark",
"imageUrl": "https://example.image/3.jpg"
}
}
Codable struct
struct ProfileData: Codable {
let name: String
let imageUrl: String
enum CodingKeys: String, CodingKey {
case name, imageUrl
}
}
I usually use dataTaskPublihser
to get an array of Profiles([Profile]
) and image(UIImage
) like:
func getProfileData() -> AnyPublisher<[ProfileData], Error> {
let exampleUrl = URL(string: "https://exmaple.com")!
return URLSession.shared.dataTaskPublisher(for: exampleUrl)
.tryMap { (data, response) -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
.decode(type: [Profile].self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
func getImage(url: String) -> AnyPublisher<UIImage, Error> {
let exampleUrl = URL(string: url)!
return URLSession.shared.dataTaskPublisher(for: exampleUrl)
.tryMap { (data, response) -> Data in
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
.map { UIImage(data: $0) ?? UIImage(systemName: "xmark")! }
.eraseToAnyPublisher()
}
}
But it has a problem that I should download the actual image data after the first network request(getProfileData
at this time) manually.
I made a sample case using the method I mentioned right before. In here, a cell initializes the view model when it's shown on the screen, so the image loads too slowly. In addition, it creates unnecessary network requests because the text field refreshes the entire view every time you type a character(because the variance test
is @State
).
So I'm wondering are there any ways to create the following:
// New struct which has UIImage, not URL
struct Profile {
let id = UUID()
let name: String
let image: UIImage
}
func getProfile() -> AnyPublisher<[Profile], Error> { ... }
Not sure if this is the best way, but it seems to solve the problem.
Please let me know if there is a better way. You don't even have to provide detailed solutions. I will appreciate it even if you give me just keywords or links.
Thank you.