Вызов API для каждого элемента массива после получения элементов из API.IOS

Программируем под IOS
Ответить
Anonymous
 Вызов API для каждого элемента массива после получения элементов из API.

Сообщение Anonymous »

Теперь у меня есть следующее: страница загружается, API вызывается и возвращает список элементов. Когда API вернется, я хочу вызвать API для каждого отдельного элемента, чтобы запросить дополнительные данные. Я застрял на этой последней части.
Вот что у меня есть на данный момент.

Код: Выделить всё

struct MainView: View {
@Environment(\.dismiss) var dismiss

let source: String
let sourceId: String

@EnvironmentObject var startupViewModel: StartupViewModel

@StateObject var vm = PlaceViewModel(placeService: PlaceService())

var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack {
ForEach(Array((vm.attractions).enumerated()), id: \.element.id) { index, place in
PlaceCardView(place: place)
}
}
}
}
}
.task {
vm.fetchPlace(source: source, sourceId: sourceId)
}
.onChange(of: vm.place != nil) {
if let placeId = vm.place?.id {
vm.fetchAttractions(placeId: placeId, page: 1)
}
}
}
.environmentObject(vm)
}
}
Как видите, при загрузке страницы я сначала вызываю fetchPlace(). Затем, когда vm.place изменился, я вызываю vm.fetchAttractions().
Вот как эти вызовы выполняются в моей модели представления:< /p>

Код: Выделить всё

class PlaceViewModel: ObservableObject {
private var cancellables = Set()
let placeService: PlaceServiceProtocol

@Published var place: Place?
@Published var attractions: [Place] = []

init(placeService: PlaceServiceProtocol) {
self.placeService = placeService
}

func fetchPlace(source: String, sourceId: String) {
placeService.getPlace(source: source, sourceId: sourceId)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { data in
print("Received:")
print(data)
}, receiveValue: {[weak self] data in
self?.place = data.data?.place
}).store(in: &cancellables)
print(cancellables)
}

func fetchAttractions(placeId: Int, page: Int, completion: ((_ response: [Place]) -> Void)? = nil) {
placeService.getAttractions(placeId: placeId, page: page)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { data in
print("Received \(data)")
}, receiveValue: {[weak self] data in
guard let newAttractions = data.data?.attractions else {
return
}

if (newAttractions.isEmpty) {
self?.hasMoreAttractions = false
}

self?.attractions.append(contentsOf: newAttractions)

completion?(newAttractions)
}).store(in: &cancellables)
print(cancellables)
}
}
А вот мой сетевой уровень (из этого урока):

Код: Выделить всё

protocol PlaceServiceProtocol {
func getPlace(source: String, sourceId: String) -> AnyPublisher
func getAttractions(placeId: Int, page: Int) -> AnyPublisher
}

class PlaceService: PlaceServiceProtocol {
let apiClient = URLSessionAPIClient
()

func getPlace(source: String, sourceId: String) -> AnyPublisher {
return apiClient.request(.getPlace(source: source, sourceId: sourceId))
}

func getAttractions(placeId: Int, page: Int) -> AnyPublisher {
return apiClient.request(.getAttractions(placeId: placeId, page: page))
}
}

Код: Выделить всё

enum PlaceEndpoint: APIEndpoint {
case getPlace(source: String, sourceId: String)
case getAttractions(placeId: Int, page: Int)

var path: String {
switch self {
case .getPlace:
return "/place/get"
case .getAttractions:
return "/place/attractions/get"
}
}

var method: HTTPMethod {
switch self {
case .getPlace:
return .post
case .getAttractions:
return .post
}
}

var headers: [String: String]? {
switch self {
case .getPlace:
return nil
case .getAttractions:
return nil
}
}

var parameters: [String: Any]? {
switch self {
case .getPlace(let source, let sourceId):
return ["source": source, "source_id": sourceId]
case .getAttractions(let placeId, let page):
return ["place_id": placeId, "page": page]
}
}
}

Код: Выделить всё

protocol APIClient {
associatedtype EndpointType: APIEndpoint
func request(_ endpoint: EndpointType) -> AnyPublisher
}

class URLSessionAPIClient: APIClient {
func request(_ endpoint: EndpointType) -> AnyPublisher  {
let url = URL(string: BuildConfiguration.shared.baseURL)!.appendingPathComponent(endpoint.path)
var request = URLRequest(url: url)

let keychain = KeychainSwift()

request.httpMethod = endpoint.method.rawValue

request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

// Authorization
request.addValue("Bearer " + (keychain.get("token") ?? ""), forHTTPHeaderField: "Authorization")

do {
// convert parameters to Data and assign dictionary to httpBody of request
request.httpBody = try JSONSerialization.data(withJSONObject: endpoint.parameters ?? [], options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}

endpoint.headers?.forEach { request.addValue($0.value, forHTTPHeaderField: $0.key) }

return URLSession.shared.dataTaskPublisher(for: request)
.subscribe(on: DispatchQueue.global(qos: .background))
.tryMap { data, response -> JSONDecoder.Input in
print(response)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw APIError.invalidResponse
}
return data
}
.decode(type: T.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
После вызова fetchAttractions() я хочу снова вызвать API, чтобы получить дополнительные данные для каждого аттракциона в списке.
Как лучше всего это сделать? Я пробовал перебирать каждый аттракцион внутри fetchAttractions(), однако при прокрутке возникала задержка.


Подробнее здесь: https://stackoverflow.com/questions/790 ... s-from-api
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»