Я работаю с данными API, которые возвращают данные JSON, которые трудно декодировать.Призыв API для партии котировки акций.Когда вызывается одна кавычка (не пакетная), результат легко декодируется JSON с использованием простой структуры.Однако в пакетном режиме версия с одинарными кавычками сгруппирована еще в два уровня, которые я не могу декодировать.В целях упрощения чтения я просто вставлю начальные фрагменты данных, чтобы проиллюстрировать проблему.
Единичная кавычка JSON:
{"symbol":"AAPL","companyName":"Apple Inc.","primaryExchange":"Nasdaq Global Select",
Итак, это легко... ключ, пары значений от начала, но в пакетном режиме это становится:
{"AAPL":{"quote":{"symbol":"AAPL","companyName":"Apple Inc.","primaryExchange":"Nasdaq Global Select",
, а затем позже в этом же результате будет вторая или третья или более кавычка, например.
}},"FB":{"quote":{"symbol":"FB","companyName":"Facebook Inc.","primaryExchange":"Nasdaq Global Select",
Так что на самом высоком уровне это не ключ, а значение.А второй уровень - это заполнитель типа метаданных для цитаты (потому что вы также можете запросить другие массивы подэлементов, такие как компания, диаграммы и т. Д.). Я не могу думать о том, как обрабатывать внешние группировки, особенно символы акций AAPL и FB.... как внешние элементы.Есть какие-нибудь мысли?
Я пошел по пути JSONSerialization, который создает строку, которую я также не могу получить в пригодной для использования форме.
Для этого я использую:
let tkrs = "C,DFS"
var components = URLComponents()
components.scheme = "https"
components.host = "api.iextrading.com"
components.path = "/1.0/stock/market/batch"
let queryItemSymbols = URLQueryItem(name: "symbols", value: "\(tkrs)")
let queryItemTypes = URLQueryItem(name: "types", value: "quote")
components.queryItems = [queryItemSymbols,queryItemTypes]
let session = URLSession.shared
let task = session.dataTask(with: components.url!) {(data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
print(json)
, который выдает:
["C": {
quote = {
avgTotalVolume = 17386485;
calculationPrice = tops;
change = "1.155";
changePercent = "0.0181";
close = "63.8";
closeTime = 1540411451191;
companyName = "Citigroup Inc.";
, и есть еще данные, но я сокращаю их.
URL-адреса API:
Пример одинарной кавычки:
https://api.iextrading.com/1.0/stock/aapl/quote
Пример пакетной кавычки:
https://api.iextrading.com/1.0/stock/market/batch?symbols=aapl,fb&types=quote
Структура, которую я успешно использовал для одинарных кавычек, работаеткрасиво с простой строкой кода:
let quote = try JSONDecoder().decode(Quote.self,from: data)
где Цитата является структурой:
struct Quote: Decodable {
let symbol: String
let companyName: String
let primaryExchange: String
let sector: String
let calculationPrice: String
let open: Double
let openTime: Int
let close: Double
let closeTime: Int
let high: Double
let low: Double
let latestPrice: Double
let latestSource: String
let latestTime: String
let latestUpdate: Int
let latestVolume: Double
let iexRealtimePrice: Double?
let iexRealtimeSize: Double?
let iexLastUpdated: Int?
let delayedPrice: Double
let delayedPriceTime: Int
let extendedPrice: Double
let extendedChange: Double
let extendedChangePercent: Double
let extendedPriceTime: Int
let previousClose: Double
let change: Double
let changePercent: Double
let iexMarketPercent: Double?
let iexVolume: Double?
let avgTotalVolume: Double
let iexBidPrice: Double?
let iexBidSize: Double?
let iexAskPrice: Double?
let iexAskSize: Double?
let marketCap: Double
let peRatio: Double?
let week52High: Double
let week52Low: Double
let ytdChange: Double
}
Редактировать: на основе предоставленного ответа
Работая на игровой площадке, это работаетхорошо с пакетными данными:
func getPrices(){
let tkrs = "AAPL,FB,C,DFS,MSFT,ATVI"
var components = URLComponents()
components.scheme = "https"
components.host = "api.iextrading.com" ///1.0/stock/market/batch
components.path = "/1.0/stock/market/batch"
let queryItemSymbols = URLQueryItem(name: "symbols", value: "\(tkrs)")
let queryItemTypes = URLQueryItem(name: "types", value: "quote")
components.queryItems = [queryItemSymbols,queryItemTypes]
let data = try! Data(contentsOf: components.url!)
do {
let response = try JSONDecoder().decode([String:[String: Quote]].self,from: data)
let tickers = ["AAPL","FB","C","DFS","MSFT","ATVI"]
for tk in tickers {
let quote = response[tk]
let price = quote!["quote"]
print("\(price!.symbol) \(price!.latestPrice)")
}
} catch let jsonErr { print("Error decoding json:",jsonErr)}
}
Но это решает мою первоначальную проблему получения ответа от URLSession только для одной цитаты.Теперь я могу просматривать массив символов акций и обновлять последнюю цену для каждого элемента с помощью этой функции.
func getPrice(ticker: String) -> Double {
var price = 0.0
let urlString = "https://api.iextrading.com/1.0/stock/\(ticker)/quote"
let data = try! Data(contentsOf: URL(string: urlString)!)
do {
let response = try JSONDecoder().decode(Quote.self,from: data)
price = response.latestPrice
} catch let jsonErr { print("Error decoding JSON:",jsonErr)}
return price
}
Итак, я перебираю массив открытых сделок с акциями и устанавливаю цену следующим образом...
opentrades[rn].trCurPrice = getPrice(ticker: opentrades[rn].trTicker)
И это прекрасно работает в моем приложении.Хотя я немного волнуюсь о том, как это будет работать во время высокой задержки.Я осознаю, что мне нужен некоторый контроль ошибок, и буду работать над тем, чтобы интегрировать это в будущее.
Редактирование / обновление: на основе отзыва используется подход, который я использую.
Создан класс, который будетделегат, который принимает массив открытых сделок и обновляет цены.
import Foundation
protocol BatchQuoteManagerDelegate {
func didLoadBatchQuote()
}
class BatchQuoteManager {
var openPositions = [OpenTradeDur]()
var delegate: BatchQuoteManagerDelegate? = nil
func getBatchQuote(tickers: [OpenTradeDur]) {
var tkrs = ""
for tk in tickers {
tkrs = tkrs + "\(tk.trTicker),"
}
var components = URLComponents()
components.scheme = "https"
components.host = "api.iextrading.com"
components.path = "/1.0/stock/market/batch"
let queryItemSymbols = URLQueryItem(name: "symbols", value: "\(tkrs)")
let queryItemTypes = URLQueryItem(name: "types", value: "quote")
components.queryItems = [queryItemSymbols,queryItemTypes]
let session = URLSession.shared
let task = session.dataTask(with: components.url!) {(data,response,error) in
guard let data = data, error == nil else { return }
let response = try! JSONDecoder().decode([String:[String: Quote]].self,from: data)
for i in 0..<tickers.count {
let quote = response[tickers[i].trTicker]
let price = quote!["quote"]
tickers[i].trCurPrice = price!.latestPrice
}
self.openPositions = tickers
if let delegate = self.delegate {
DispatchQueue.main.async {
delegate.didLoadBatchQuote()
}
}
}
task.resume()
}
}
Затем я расширяю свой ViewController с помощью BatchQuoteManagerDelegate
, реализую метод func didLoadBatchQuote()
, где я получаю обновленные цены через массив BatchQuoteManager.openPositions
,Мне просто нужно было определить let batchQuoteManager = BatchQuoteManager()
в моем ViewController и в viewDidLoad()
включить оператор batchQuoteManager.delegate = self
.Как только я узнаю, что все необходимые данные загружены в мой ViewController, я вызываю функцию, чтобы получить цены (в конце viewDidLoad()
) с batchQuoteManager.getBatchQuote(tickers: opentrades)
И это все.Пока это работает очень хорошо.