I have majority of the functionality working and returning exactly what I want. However, I'm having bit of a brain fart when it comes to taking the photos
array in the response
and assigning them to appropriate employees
to be able to render them. Here's what's going on:
- There are 4x Codable structs:
Response
,Company
,Employee
, andProfileImages
.Response
is the main object returned by the API and then decoded intoCompany
having an array of[Employee]
, each have 3xProfileImages
(small, medium, and large in size) - There's a
companyPublisher
that fetches thecompany
details along with an array ofemployees
- Then there's a
photosPublisher
which takesemployees
array from the previous step and sequences them to be able to retrieve theirprofileImages.large
profile image - At last, I have a
Publishers.Zip(companyPublisher, photosPublisher)
that sets up the publisher's.sink()
to respond with completion once everything requested has been fetched.
Can someone advise what would be the appropriate steps I need to take to be able to assign the correct employee image to the actual employee? I was thinking about setting up an optional UIImage
type property
inside the Employee
codable struct but am still unsure on how I would go about assigning the appropriate Future object to that employee.
Any help would be greatly appreciated. Thanks in advance!
Response.JSON
:
{
"success": true,
"company": {
"id": 64,
"name": "XYZ (Birmingham, AL)",
"enabled": true
},
"employees": [{
"id": 35,
"name": "Chad Hughes",
"email": "chad.hughes@company.com",
"profileImages": {
"small": "https://via.placeholder.com/150/09f/fff.png",
"medium": "https://via.placeholder.com/300/09f/fff.png",
"large": "https://via.placeholder.com/600/09f/fff.png"
}
}, {
"id": 36,
"name": "Melissa Martin",
"email": "melissa.martin@company.com",
"profileImages": {
"small": "https://via.placeholder.com/150/F2A/fff.png",
"medium": "https://via.placeholder.com/300/F2A/fff.png",
"large": "https://via.placeholder.com/600/F2A/fff.png"
}
}]
}
Models.swift
(Codable Structs):
struct Response: Codable {
let success: Bool
let company: Company
let employees: [Employee]
let message: String?
}
struct Company: Codable, Identifiable {
let id: Int
let name: String
let enabled: Bool
}
struct Employee: Codable, Identifiable {
let id: Int
let name: String
let email: String
let profileImages: ProfileImage
let profileImageToShow: SomeImage?
}
struct SomeImage: Codable {
let photo: Data
init(photo: UIImage) {
self.photo = photo.pngData()!
}
}
struct ProfileImage: Codable {
let small: String
let medium: String
let large: String
}
CompanyDetails.swift
:
class CompanyDetails: ObservableObject {
private let baseURL = "https://my.api.com/v1"
@Published var company: Company = Company()
@Published var employees: [Employee] = []
var subscriptions: Set<AnyCancellable> = []
func getCompanyDetails(company_id: Int) {
let url = URL(string: "\(baseURL)/company/\(company_id)")
// Company Publisher that retrieves the company details and its employees
let companyPublisher = URLSession.shared.dataTaskPublisher(for url: url)
.map(\.data)
.decode(type: Response.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
// Photo Publisher that retrieves the employee's profile image in large size
let photosPublisher = companyPublisher
.flatMap { response -> AnyPublisher<Employee, Error> in
Publishers.Sequence(sequence: response.employees)
.eraseToAnyPublisher()
}
.flatMap { employee -> AnyPublisher<UIImage, Error> in
URLSession.shared.dataTaskPublisher(for url: URL(string: employee.profileImages.large)!)
.compactMap { UIImage(data: $0.data) }
.mapError { $0 as Error }
.eraseToAnyPublisher()
}
.collect()
.eraseToAnyPublisher()
// Zip both Publishers so that all the retrieved data can be .sink()'d at once
Publishers.Zip(companyPublisher, photosPublisher)
.receive(on: DispatchQueue.main)
.sink(
receiveCompletion: { completion in
print(completion)
},
receiveValue: { company, photos in
print(company)
print(photos)
}
)
.store(in: &subscriptions)
}
}