который это уже отформатированная строка валюты

аюсь обновить метки данными, извлеченными из API, чтобы напечатать цену биткойна и процентное изменение в приложении, но я не могу понять, как правильно декодировать JSON.

BitcoinInfo.swift:

import Foundation
import UIKit

struct Bitcoin: Codable {
    let percentChange1h: String
    let priceEUR: String
    private enum CodingKeys: String, CodingKey {
        case percentChange1h = "percent_change_1h", priceEUR = "price_eur"
    }
}

extension Bitcoin {
    var priceEURdecimal: Decimal {
        return Decimal(string: priceEUR) ?? 0
    }
    var priceEURcurrency: String {
        Formatter.currency.locale = Locale(identifier: "fr_FR")
        return Formatter.currency.string(for: priceEURdecimal) ?? ""
    }
}

ViewController.swift:

import Foundation
import UIKit

class ViewController: UIViewController {

    let bitcoinInfoController = BitcoinInfoController()

    @IBOutlet weak var bitcoinPriceLabel: UILabel!
    @IBOutlet weak var bitcoinPercentageChangeLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        bitcoinPriceLabel.text = ""
        bitcoinPercentageChangeLabel.text = ""

        fetchBitcoinInfo { bitcoin, error in
            guard let bitcoin = bitcoin else {
                print(error!);
                return
            }
            self.updateUI(with: bitcoin)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    func updateUI(with bitcoinInfo: Bitcoin) {
        DispatchQueue.main.async {
            self.bitcoinPriceLabel.text = bitcoinInfo.priceEURcurrency
            self.bitcoinPercentageChangeLabel.text = String(format: "%.2f%%", Double(bitcoinInfo.percentChange1h) ?? 0)
        }
    }

    func fetchBitcoinInfo(completion: @escaping (Bitcoin?, Error?) -> Void) {
        let baseURL = URL(string: "https://api.coinmarketcap.com/v1/ticker/bitcoin/?convert=EUR")!

        let url = baseURL

        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else { return }
            do {
                if let bitcoinEUR = try JSONDecoder().decode([Bitcoin].self, from: data).first {
                    print(bitcoinEUR)
                    print(bitcoinEUR.priceEUR)
                    print(bitcoinEUR.priceEURdecimal)
                    print(bitcoinEUR.priceEURcurrency)
                    print(bitcoinEUR.percentChange1h)
                    completion(bitcoinEUR, nil)
                }
            } catch {
                print(error)
            }
        }
        task.resume()
    }

}

Консоль печатает «Либо данные не были возвращены, либо данные не были должным образом декодированы».

РЕДАКТИРОВАТЬ: Это последняя версия кода. EDIT2: код теперь на 100% функциональный! :)

 Leo Dabus26 дек. 2017 г., 18:14
@Wizzardzz обратите внимание, что вы отображаете цену в евро с помощью символа валюты доллара.
 Wizzardzz 26 дек. 2017 г., 17:37
Спасибо, Хэмиш, ты не первый, кто мне это говорит. Я изучаю Swift через курс Apple, и они учат с попыткой? на первом месте. Плохая привычка меняться для меня
 Hamish26 дек. 2017 г., 17:35
Не выбрасывайте ошибку декодирования сtry? - поймать ошибку и распечатать ее; он точно скажет вам, в чем проблема.

Ответы на вопрос(1)

Решение Вопроса

а процент - это строки, а не пары. Кстати, он возвращает массив, поэтому вам нужно использовать[Bitcoin].self введите при декодировании:

Вот как должна выглядеть ваша кодируемая структура:

struct Bitcoin: Codable {
    let id: String
    let name: String
    let symbol: String
    let rank: String
    let priceUSD: String
    let priceBTC: String
    let volume24hUSD: String
    let marketCapUSD: String
    let availableSupply: String
    let totalSupply: String
    let maxSupply: String
    let percentChange1h: String
    let percentChange24h: String
    let percentChange7d: String
    let lastUpdated: String
    let priceEUR: String
    let volume24hEUR: String
    let marketCapEUR: String
    private enum CodingKeys: String, CodingKey {
        case id, name, symbol, rank,
        priceUSD = "price_usd",
        priceBTC = "price_btc",
        volume24hUSD = "24h_volume_usd",
        marketCapUSD = "market_cap_usd",
        availableSupply = "available_supply",
        totalSupply = "total_supply",
        maxSupply = "max_supply",
        percentChange1h = "percent_change_1h",
        percentChange24h = "percent_change_24h",
        percentChange7d = "percent_change_7d",
        lastUpdated = "last_updated",
        priceEUR = "price_eur",
        volume24hEUR = "24h_volume_eur",
        marketCapEUR = "market_cap_eur"
    }
}

И вот как вы должны декодировать массив json, возвращаемый API, и получить его первый элемент:

do {
    if let bitcoinEUR = try JSONDecoder().decode([Bitcoin].self, from: data).first {
        print(bitcoinEUR)
        print(bitcoinEUR.priceEUR)
        print(bitcoinEUR.percentChange1h)
    }
} catch {
    print(error)
}

Если вас интересуют только эти два свойства, вы можете установить свою структуру биткойнов следующим образом:

struct Bitcoin: Codable {
    let percentChange1h: String
    let priceEUR: String
    private enum CodingKeys: String, CodingKey {
        case percentChange1h = "percent_change_1h", priceEUR = "price_eur"
    }
}

редактировать / обновление:

Примечание. Вы отображаете цену в евро с помощью символа валюты доллара. Если вам нужно отформатировать цену в евро с двумя цифрами дроби, вам необходимо сначала инициализировать новый объект с плавающей запятой строкой, возвращаемой API.

Таким образом, вы можете расширить API Биткойн двумя вычисленными свойствами: одно для преобразования строки цены евро в десятичную, а другое для форматирования десятичного значения в валюте:

extension Bitcoin {
    var priceEURdecimal: Decimal {
        return Decimal(string: priceEUR) ?? 0
    }
    var priceEURcurrency: String {
        Formatter.currency.locale = Locale(identifier: "fr_FR")
        return Formatter.currency.string(for: priceEURdecimal) ?? ""
    }
}

Вам также необходимо добавить эти расширения в новый файл Swift в вашем проекте, чтобы помочь вам отформатировать валюту:

extension NumberFormatter {
    convenience init(numberStyle: Style) {
        self.init()
        self.numberStyle = numberStyle
    }
}
extension Formatter {
    static let currency = NumberFormatter(numberStyle: .currency)
}

Использование:

do {
    if let bitcoinEUR = try JSONDecoder().decode([Bitcoin].self, from: data).first {
        print(bitcoinEUR.priceEURdecimal)   // "13823.952495\n"
        print(bitcoinEUR.priceEURcurrency)  // "13 823,95 €\
    }
} catch {
    print(error)
}
 Leo Dabus26 дек. 2017 г., 19:00
Просто переместитеfetchBitcoinInfo метод изBitcoinInfoController на вашViewController и изменить наself.bitcoinPriceLabel.text = bitcoinObjectName.priceEURcurrency который это уже отформатированная строка валюты
 Wizzardzz 26 дек. 2017 г., 18:55
Еще раз спасибо, Лео, французский идеален! Я собираюсь попросить вас об одном последнем сервисе и помочь мне обновить метки в моем ViewController, я не могу понять, как заставить их отображать извлеченные данные
 Wizzardzz 26 дек. 2017 г., 18:31
Большое спасибо, Лео! Что такое плавающая точка? Я до сих пор не могу понять, как обновить метки с данными
 Leo Dabus26 дек. 2017 г., 18:32
Плавающая точка может быть Double или Float. Вы также можете использовать десятичный тип. В какой локали вы хотите отформатировать валюту? Валюта евро имеет много разных способов форматирования в зависимости от локали (Франция, Германия и т. Д.)
 Leo Dabus26 дек. 2017 г., 18:43
Я использовал французский язык, но вы можете изменить его в соответствии с вашими потребностями.

Ваш ответ на вопрос