Я использую некоторые API для получения данных. Они инициируются как session.dataTask, и я использую классы для инкапсуляции вызовов API, методов и возвращаемых свойств для каждого отдельного API. Как мне настроить мой код так, чтобы обновлять соответствующие метки экрана и подвиды после завершения сеансов API и получения данных?
Соответствующий раздел init класса AstronomicalTimes:
init (date: Date, lat: Float, long: Float) {
let coreURL = "https://api.sunrise-sunset.org/json?"
let position = "lat=\(lat)&lng=\(long)"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateString = "&date=" + dateFormatter.string(from: date)
//let dateString = "&date=2020-06-21"
let urlString = coreURL + position + dateString + "&formatted=0"
let session = URLSession.shared
let url = URL(string: urlString)!
let request = URLRequest(url: url)
session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
let nsError = error as NSError
print("Astronomical Times API call failed with error \(nsError.code)")
return
}
if let response = response as? HTTPURLResponse {
print("Astronomical Times API call response is \(response.statusCode)")
}
if let data = data {
do {
let astronomicalTimesResponse = try JSONDecoder().decode(AstronomicalTimesResponse.self, from: data)
print("Astronomical times successfully parsed")
self.fillFields(astronomicalTimesResponse.results) //completes all class properties from parsed data
} catch {
print("Error while tide details parsing: \(error)")
}
}
}).resume()
Метке присваивается результат вызова API в viewDidLoad () с:
currentAstronomicalTimes = AstronomicalTimes(date: savedDate, lat: currentSelection.station.lat, long: currentSelection.station.long)
lblAstDawn.text = currentAstronomicalTimes.strings.astronomicalTwilightBegin
Понятно, что это не работает, поскольку экран отображается с пустыми надписями и метками до того, как API вернет данные. Я не могу понять, как сигнализировать ViewController о завершении API, а затем как перерисовать метки и т. Д. c. Я попытался обновить поля viewController в выражении закрытия вызовов API, но я не могу обновить UILabels из другого класса (и я думаю, что этот подход является грязным, так как logi update logi c действительно должен быть в ViewController)
Любая помощь приветствуется.
ОБНОВЛЕНИЕ после комментариев Роба:
Я изменил определение класса в соответствии с рекомендациями и успешно загружает данные из API. Определение класса приведено ниже: обратите внимание, я добавляю функцию, которая берет загруженные данные и превращает их в строки времени и date () для простоты использования в viewController (все они, по-видимому, правильно заполнены после вызова API)
import Foundation
enum AstronomicalTimesError: Ошибка {case invalidResponse (Data ?, URLResponse?)}
class AstronomicalTimes {
//structures for decoding daylight times
struct AstronomicalTimesResponse: Decodable {
public var results: AstronomicalTimes
public var status: String
}
struct AstronomicalTimes: Decodable {
var sunrise = String()
var sunset = String()
var solarNoon = String()
var dayLength = 0
var civilTwilightBegin = String()
var civilTwilightEnd = String()
var nauticalTwilightBegin = String()
var nauticalTwilightEnd = String()
var astronomicalTwilightBegin = String()
var astronomicalTwilightEnd = String()
private enum CodingKeys : String, CodingKey {
case sunrise = "sunrise"
case sunset = "sunset"
case solarNoon = "solar_noon"
case dayLength = "day_length"
case civilTwilightBegin = "civil_twilight_begin"
case civilTwilightEnd = "civil_twilight_end"
case nauticalTwilightBegin = "nautical_twilight_begin"
case nauticalTwilightEnd = "nautical_twilight_end"
case astronomicalTwilightBegin = "astronomical_twilight_begin"
case astronomicalTwilightEnd = "astronomical_twilight_end"
}
}
//used to hold string values to enter to label, i.e. time strings for labels
var strings = AstronomicalTimes()
//struct and variable used to hold specific date/times for gradient calculation
struct Times {
var sunrise = Date()
var sunset = Date()
var solarNoon = Date()
var dayLength = 0
var civilTwilightBegin = Date()
var civilTwilightEnd = Date()
var nauticalTwilightBegin = Date()
var nauticalTwilightEnd = Date()
var astronomicalTwilightBegin = Date()
var astronomicalTwilightEnd = Date()
}
var times = Times()
let date: Date
let latitude: Float
let longitude: Float
init (date: Date, latitude: Float, longitude: Float) {
self.date = date
self.latitude = latitude
self.longitude = longitude
}
func start(completion: @escaping (Result<AstronomicalTimesResponse, Error>) -> Void) {
var components = URLComponents(string: "https://api.sunrise-sunset.org/json")!
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // just in case your end user isn't using Gregorian calendar
dateFormatter.dateFormat = "yyyy-MM-dd"
components.queryItems = [
URLQueryItem(name: "lat", value: "\(latitude)"),
URLQueryItem(name: "lng", value: "\(longitude)"),
URLQueryItem(name: "date", value: dateFormatter.string(from: date)),
URLQueryItem(name: "formatted", value: "0")
]
let session = URLSession.shared
let url = components.url!
let request = URLRequest(url: url)
session.dataTask(with: request) { data, response, error in
if let error = error {
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
DispatchQueue.main.async {
completion(.failure(AstronomicalTimesError.invalidResponse(data, response)))
}
return
}
do {
print("Astronomical times api completed with status code ", httpResponse.statusCode)
let astronomicalTimesResponse = try JSONDecoder().decode(AstronomicalTimesResponse.self, from: responseData)
DispatchQueue.main.async {
completion(.success(astronomicalTimesResponse))
self.fillFields(astronomicalTimesResponse.results)
}
} catch let jsonError {
DispatchQueue.main.async {
completion(.failure(jsonError))
}
}
}.resume()
}
func fillFields(_ input: AstronomicalTimes) -> Void {
//formats output fields into Date() or String (HH:mm) format
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" //Your date format
times.sunrise = dateFormatter.date(from: input.sunrise) ?? Date()
times.sunset = dateFormatter.date(from: input.sunset) ?? Date()
times.solarNoon = dateFormatter.date(from: input.solarNoon) ?? Date()
times.dayLength = input.dayLength
times.civilTwilightBegin = dateFormatter.date(from: input.civilTwilightBegin) ?? Date()
times.civilTwilightEnd = dateFormatter.date(from: input.civilTwilightEnd) ?? Date()
times.nauticalTwilightBegin = dateFormatter.date(from: input.nauticalTwilightBegin) ?? Date()
times.nauticalTwilightEnd = dateFormatter.date(from: input.nauticalTwilightEnd) ?? Date()
times.astronomicalTwilightBegin = dateFormatter.date(from: input.astronomicalTwilightBegin) ?? Date()
times.astronomicalTwilightEnd = dateFormatter.date(from: input.astronomicalTwilightEnd) ?? Date()
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm"
strings.sunrise = timeFormatter.string(from: times.sunrise)
strings.sunset = timeFormatter.string(from: times.sunset)
strings.solarNoon = timeFormatter.string(from: times.solarNoon)
strings.dayLength = input.dayLength
strings.civilTwilightBegin = timeFormatter.string(from: times.civilTwilightBegin)
strings.civilTwilightEnd = timeFormatter.string(from: times.civilTwilightEnd)
strings.nauticalTwilightBegin = timeFormatter.string(from: times.nauticalTwilightBegin)
strings.nauticalTwilightEnd = timeFormatter.string(from: times.nauticalTwilightEnd)
strings.astronomicalTwilightBegin = timeFormatter.string(from: times.astronomicalTwilightBegin)
strings.astronomicalTwilightEnd = timeFormatter.string(from: times.astronomicalTwilightEnd)
}
}
Затем я вызываю это из функция внутри viewController:
func getAstronomicalTimes(date: Date, latitude: Float, longitude: Float) -> Void {
let astronomicalTimes = AstronomicalTimes(date: date, latitude: latitude, longitude: longitude)
astronomicalTimes.start { result in
switch result {
case .success(let astronomicalTimesResponse):
print("astronomical times response ", astronomicalTimesResponse)
print("label", astronomicalTimes.strings.astronomicalTwilightBegin)
self.lblAstDawn.text = astronomicalTimes.strings.astronomicalTwilightBegin
case .failure(let error):
print(error)
}
}
}
Эта функция вызывается из viewDidLoad ():
getAstronomicalTimes(date: savedDate, latitude: currentSelection.station.lat, longitude: currentSelection.station.long)
Однако getAstronomicalTimes (date: latitude: longitude) не обновляет lblAstDawn.text как я и надеялся.
Какие-нибудь подсказки относительно того, где я это неправильно понял?