Функция декодирования JSON из файла по сравнению с веб-сайтом

Предполагается, что SwiftUI упрощает работу — я немного расстроен, так как несколько недель работаю над URLSession+JSONDecoder, мне действительно нужна помощь!

У меня есть функция загрузки данных JSON из файла в Swift, и она работает так, как ожидалось. Я скопировал / вставил функцию и обновил ее, чтобы получить данные через API, однако я получаю ошибку времени компиляции: «Неожиданное непустое возвращаемое значение в функции void». Является ли мой подход неправильным, чтобы использовать функцию для JSON через Интернет?

JSON-ответ:

{
"T":"CSU",
"v":468303,
"vw":1.2838,
"o":1.31,
"c":1.24,
"h":1.38,
"l":1.2001,
"t":1607374800000,
"n":994
}

struct Root2: Codable {
    var T: String
    var v: Double
    var vw: Double
    var o: String
    var c: String
    var h: Double
    var l: Double
    var t: Double
}

Эта файловая функция работает так, как ожидалось:

let symbolData: [Root2] = load("symbolData.json")

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

Для веб-версии я получаю ошибку времени компиляции: «Неожиданное непустое возвращаемое значение в функции void». Строка: return try decoder.decode(T.self, from: data)

func loadURL<T: Decodable>() -> T {
    
    guard let url = URL(string: """)
    else {
        fatalError("Invalid URL in main bundle.")
    }
    let request = URLRequest(url: url)
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        do {
            if let data = data {
                let stringData = String(decoding: data, as: UTF8.self)
                print("1 Fetched: \(url)")
                print("2 Response: \(stringData)")
                
                
                let decoder = JSONDecoder()
                return try decoder.decode(T.self, from: data)
            }
        }
        catch {
            fatalError("Couldn't parse as :\n\(error)")
        }
    }.resume()
}

Рабочая версия после помощи Лео!

class Manager: ObservableObject {
    @Published var symbols: [Symbol] = []
   
    func loadURL<T: Decodable>(using decoder: JSONDecoder = .msSince1970, completion: @escaping ((T?, Error?) -> Void)) {
        let url = URL(string: """)!
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else {
                print("ops")
                completion(nil, error)
                return
            }
            print("1 Fetched: \(url)")
            print("2 Response:", String(data: data, encoding: .utf8) ?? "")
            _ = Data("""
            [
              {
                "open": {
                  "price": 124.02,
                  "time": 1657105851499
                },
                "close": {
                  "price": 124.96,
                  "time": 1618647822184
                },
                "high": 124.64,
                "low": 124.65,
                "volume": 75665274,
                "symbol": "AAPL"
              }
            ]
            """.utf8)
            do {
                completion(try decoder.decode(T.self, from: data), nil)
                //completion(try decoder.decode(T.self, from: tempDataForTesting), nil)
            } catch {
                completion(nil, error)
            }
        }.resume()
    }
}

extension JSONDecoder {
    static let msSince1970: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .millisecondsSince1970
        return decoder
    }()
}

Я немного сбит с толку: этот вопрос идентичен последнему вопросу, который вы разместили, здесь, за исключением просьбы не закрывать как дубликат (которую я удалил, так как это не относится к заголовку или описанию вопрос). Если вы считаете, что ваш первоначально закрытый вопрос не является дубликатом того, который был помечен как дубликат, вам следует включить это в свой вопрос и объяснить, почему он не является дубликатом, чтобы он не был закрыт снова. Но публиковать идентичный вопрос не очень полезно.

David Makogon 14.12.2020 21:17

Посмотрите документацию для URLSession.dataTask, замыкание возвращает Void, это ничто, поэтому вы ничего не можете вернуть из него. Прочитайте дубликат ссылки в предыдущем вопросе.

Joakim Danielson 14.12.2020 21:40

Привет Дэвид, спасибо за ваше сообщение. Возможно, я не понимаю подход stackoverflow, так как я относительно новичок. Тем не менее, я считаю, что публикация вопроса о разнице между декодированием JSON с использованием файла и веб-версией быстрого JSONDecoder является интересным вопросом для сообщества. Вы бы не согласились?

TraderPhilDE 14.12.2020 21:56

Разница в том, что URLSession работает асинхронно, в отличие от Data(contentsOf. Этот вопрос задавался много-много раз.

vadian 14.12.2020 22:12

Основываясь на отзывах, я добавил вторую версию функции выше в вопросе. Теперь я присваиваю результаты .decode и возвращаю переменную foo (также удалил дженерики, так как это излишне усложняло функцию). Теперь выскакивает ошибка, что переменная присваивается до инициализации.

TraderPhilDE 14.12.2020 23:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
139
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете ждать, пока асинхронная задача завершится и вернет результат. Что вам нужно, так это обработчик завершения. Вам также нужно будет явно установить результирующий тип, если вы не передадите результирующий тип своему методу декодирования, и вам нужно вызвать возобновление, чтобы запустить задачу данных сеанса URL-адреса:

import SwiftUI

struct ContentView: View {
    @ObservedObject var manager = Manager()
    @State var string: String = "Hello, world!"
    var body: some View {
        Text(manager.symbol)
        .padding()
        .onAppear {
            manager.load(symbol: manager.symbol) { (symbols: [Symbol]?, error: Error?) in
                guard let symbols = symbols else {
                    print("error:", error ?? "")
                    string = "JSON could not be parsed"
                    return
                }
                for symbol in symbols {
                    print(symbol.open.price)
                    print(symbol.open.time)
                    print(symbol.close.price)
                    print(symbol.close.time)
                    print(symbol.high)
                    print(symbol.low)
                    print(symbol.volume)
                    print(symbol.symbol)
                    DispatchQueue.main.async {
                        manager.symbols = symbols
                    }
                }
                string = "JSON was successufly parsed"
            }
        }
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class Manager: ObservableObject {
    @Published var symbols: [Symbol] = []
    @Published var symbol: String = "IBM"
    func load<T: Decodable>(symbol: String, using decoder: JSONDecoder = .msSince1970, completion: @escaping ((T?, Error?) -> Void)) {
        guard let url = URLComponents(symbol: symbol).url else {
            completion(nil, URL.Error.invalidURL)
            return
        }
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else {
                print("ops")
                completion(nil, error)
                return
            }
            print("1 Fetched: \(url)")
            print("2 Symbol: \(symbol)")
            print("3 Response:", String(data: data, encoding: .utf8) ?? "")
            do {
                completion(try decoder.decode(T.self, from: data), nil)
            } catch {
                completion(nil, error)
            }
        }.resume()
    }
}

struct Symbol: Codable {
    let open, close: Price
    let high, low: Double
    let volume: Int
    let symbol: String
}

struct Price: Codable {
    let price: Double
    let time: Date
}

extension JSONDecoder {
    static let msSince1970: JSONDecoder = {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .millisecondsSince1970
        return decoder
    }()
}

extension URLComponents {
    init(scheme: String = "https",
         host: String = "sandbox.iexapis.com",
         path: String = "/stable/stock/market/ohlc",
         symbol: String,
         token: String = "YOUR_API_TOKEN") {
        self.init()
        self.scheme = scheme
        self.host = host
        self.path = path
        self.queryItems = [URLQueryItem(name: "symbols", value: symbol),
                           URLQueryItem(name: "token", value: token)]
    }
}

extension URL {
    enum Error: String, Swift.Error {
        case invalidURL = "Invalid URL"
    }
}

Это напечатает

1 Получено: https://sandbox.iexapis.com/stable/stock/market/ohlc?symbols=IBM&token=YOUR_API_TOKEN
2 Символ: IBM
3 Ответ: [{"открыть":{"цена":128,9,"время":1636600302693},"закрыть":{"цена":131,44,"время":1662259300134},"высокий":132,517,"низкий" :130.074,"том":3403359,"символ":"IBM"}]
128,9
2021-11-11 03:11:42 +0000
131,44
2022-09-04 02:41:40 +0000
132.517
130.074
3403359
IBM

Комментарии не для расширенного обсуждения; этот разговор был перемещен в чат.

Samuel Liew 17.12.2020 02:00

Другие вопросы по теме