Написание функции сериализации JSON - PullRequest
0 голосов
/ 23 октября 2018

Я хотел бы создать функцию, которая принимает несколько параметров, а затем выводит нужные мне данные из веб-API.Очевидно, что большую часть времени мне потребуется настроить его в соответствии с вариантом использования, но просто для удовольствия я пытаюсь выяснить суперосновную функцию, которая успешно анализирует JSON, примерно как половину строк кода в функции.ниже приведена общая обработка ошибок.

Например, если я обычно использую что-то вроде

func getJSON(completionHandler: @escaping (Bool) -> ()) {
    let jsonUrlString = "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808"
    guard let url = URL(string: jsonUrlString) else {return}

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data, err == nil else {
            print(err!)
            return
        }

        do {
            let response = try
                JSONDecoder().decode(TopStoriesResponse.self, from: data)

            self.storyData = response.results

            completionHandler(true)

        } catch let jsonErr {
            print("Error serializing JSON", jsonErr)
        }
    }.resume()
}

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

Могу ли я обрезатьжир на этом и сделать что-то вроде

func jsonFetcher(apiLink: String, structToDecode: String, arrayThatHoldsResponse: [String], completionHandler: @escaping (Bool) -> ()) {
    let jsonUrlString = apiLink
    guard let url = URL(string: jsonUrlString) else {return}

    URLSession.shared.dataTask(with: url) { (data, response, err) in
        guard let data = data, err == nil else {
            print(err!)
            return
        }

        do {
            let response = try
                JSONDecoder().decode(structToDecode, from: data)

            arrayThatHoldsResponse = response.results

            completionHandler(true)

        } catch let jsonErr {
            print("Error serializing JSON", jsonErr)
        }
    }.resume()
}

Я просто не уверен насчет типов данных structToDecode и arrayThatHoldsResponse (в приведенном выше примере функции я просто использовал String в качестве заполнителя), предполагая, что они выглядят как

Структура (ы)

struct TopStoriesResponse: Decodable {
    let status: String
    let results: [Story]
}

struct Story: Decodable {
    let title: String
    let abstract: String
    let url: String
    let multimedia: [Multimedia]

    private enum CodingKeys: String, CodingKey {
        case title
        case abstract
        case url
        case multimedia
    }

    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        title = try container.decode(String.self, forKey: .title)
        abstract = try container.decode(String.self, forKey: .abstract)
        url = try container.decode(String.self, forKey: .url)
        multimedia = (try? container.decode([Multimedia].self, forKey: .multimedia)) ?? []
    }

}

Массив

var storyData = [Story]()

Таким образом, я могу просто позвонить

jsonFetcher(apiLink: link, structToDecode: myStruct, arrayThatHoldsResponse: myArray, completionHandler: <#T##(Bool) -> ()#>)

Tспасибо за любую помощь!

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

На основе ответа Джейкоба я рекомендую также указать возможную ошибку.

Чтобы общий макет объявлял - также универсальный - enum в качестве типа возврата

enum FetchResult<T> {
    case success(T), failure(Error)
}

и вернуть FetchResult с переданным статическим типом

func fetchData<T: Decodable>(url: URL, completion: @escaping (FetchResult<T>) -> Void) {

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        guard let data = data else {completion(.failure(error!)); return } 
        do {
            let object = try JSONDecoder().decode(T.self, from: data)
            completion(.success(object))
        } catch {
            completion(.failure(error))
        }
    }.resume()
}

и использовать его

let jsonUrl = URL(string: "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=••••••••••••••••••:1:73741808")!

fetchData(url: jsonUrl) { (result : FetchResult<TopStoriesResponse>) in
    switch result {
    case .success(let object): print(object) // do something with object
    case .failure(let error): print(error) // handle the error
    }
}
0 голосов
/ 23 октября 2018

Сила дженериков.Вы можете сделать универсальную функцию, где параметром является urlString.T наследование протокола Decodable.

Таким образом, вы можете вызывать эту функцию каждый раз, пока ваша Модель наследует протокол Decodable.

func fetchData<T: Decodable>(urlString: String, completion: @escaping (T) -> ()) {
    let url = URL(string: urlString)!

    URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print(error.localizedDescription)
        }

        guard let data = data else { return }

        do {
            let object = try JSONDecoder().decode(T.self, from: data)
            completion(object)
        } catch let jsonErr {
            print("Failed to decode json:", jsonErr)
        }
    }.resume()
}

Как вызвать функцию:

struct User: Decodable { }

fetchData(urlString: "yourUrl") { (User: User) in
    // Handle result
}


struct Animal: Decodable { }

fetchData(urlString: "yourUrl") { (animal: Animal) in
    // Handle result
}


// Or if you want to fetch an array of users instead
fetchData(urlString: "yourUrl") { (users: [User]) in
    // Handle result
}

В вашем случае

var storiesData: [Story] = []

fetchData(urlString: "https://api.nytimes.com/svc/topstories/v1/business.json?api-key=f4bf2ee721031a344b84b0449cfdb589:1:73741808") { (stories: [Story] in
    storiesData = stories
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...