问题描述
我有以下JSON有效负载,我需要将其转换为数字并随后进行格式化以进行显示。
{
"kilometers_per_second": "14.4578929636","kilometers_per_hour": "52048.4146691173","miles_per_hour": "32340.8607703746"
}
使用Codable,我创建了以下结构:
struct RelativeVeLocity: Codable,Equatable {
let kilometersPerSecond: String?
let kilometersPerHour: String?
let milesPerHour: String?
enum CodingKeys: String,CodingKey {
case kilometersPerSecond = "kilometers_per_second"
case kilometersPerHour = "kilometers_per_hour"
case milesPerHour = "miles_per_hour"
}
}
属性是String
实例,因为这是API返回的内容,并且我正在学习首次使用视图模型,因此我想使用视图模型来转换String
实例在返回格式化的String
实例之前先转换成数字。
我的视图模型具有以下结构:
struct RelativeVeLocityviewmodel {
private let relativeVeLocity: RelativeVeLocity
init(relativeVeLocity: RelativeVeLocity) {
self.relativeVeLocity = relativeVeLocity
}
}
extension RelativeVeLocityviewmodel {
var formattedKilometersPerHour: String? {
guard
let stringValue = relativeVeLocity.kilometersPerHour,let decimalValue = Decimal(string: stringValue),let formatted = NumberFormatter.relativeVeLocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedKilometersPerSecond: String? {
guard
let stringValue = relativeVeLocity.kilometersPerSecond,let formatted = NumberFormatter.relativeVeLocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
var formattedMilesPerHour: String? {
guard
let stringValue = relativeVeLocity.kilometersPerSecond,let formatted = NumberFormatter.relativeVeLocityFormatter.string(from: decimalValue as NSNumber)
else { return nil }
return formatted
}
}
如您所见,它将String
实例转换为Decimal
实例,然后用以下Decimal
格式化NumberFormatter
实例:
extension NumberFormatter {
static let relativeVeLocityFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = .max
formatter.numberStyle = .decimal
formatter.usesGroupingSeparator = true
return formatter
}()
}
我用于测试视图模型的XCTestCase
子类是:
class Tests_RelativeVeLocityviewmodel: XCTestCase {
let kilometersPerSecond = "14.4578929636"
let kilometersPerHour = "52048.4146691173"
let milesPerHour = "32340.8607703746"
var populatedviewmodel: RelativeVeLocityviewmodel!
var emptyviewmodel: RelativeVeLocityviewmodel!
override func setUpWithError() throws {
try super.setUpWithError()
let populatedRelativeVeLocity = RelativeVeLocity(
kilometersPerSecond: kilometersPerSecond,kilometersPerHour: kilometersPerHour,milesPerHour: milesPerHour
)
populatedviewmodel = RelativeVeLocityviewmodel(relativeVeLocity: populatedRelativeVeLocity)
let emptyRelativeVeLocity = RelativeVeLocity(
kilometersPerSecond: nil,kilometersPerHour: nil,milesPerHour: nil
)
emptyviewmodel = RelativeVeLocityviewmodel(relativeVeLocity: emptyRelativeVeLocity)
}
override func tearDownWithError() throws {
emptyviewmodel = nil
populatedviewmodel = nil
try super.tearDownWithError()
}
func test_RelativeVeLocityviewmodel_ReturnsNilFormattedKilometersPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyviewmodel.formattedKilometersPerHour)
}
func test_RelativeVeLocityviewmodel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedviewmodel.formattedKilometersPerHour,expected)
}
func test_RelativeVeLocityviewmodel_ReturnsNilFormattedKilometersPerSecond_WhenValueIsMissing() {
XCTAssertNil(emptyviewmodel.formattedKilometersPerSecond)
}
func test_RelativeVeLocityviewmodel_ReturnsNilFormattedMilesPerHour_WhenValueIsMissing() {
XCTAssertNil(emptyviewmodel.formattedMilesPerHour)
}
}
以下测试...
func test_RelativeVeLocityviewmodel_ReturnsFormattedKilometersPerHour_WhenValueIsPresent() {
let expected = "52,048.4146691173"
XCTAssertEqual(populatedviewmodel.formattedKilometersPerHour,expected)
}
...产生以下故障:
XCTAssertEqual Failed: ("Optional("52,048.414669")") is not equal to ("Optional("52,048.4146691173")")
我知道我可以使用XCTAssertEqual(_:_:accuracy:_:file:line:)
,但是我想保留所有十进制值。
我做错了什么,导致格式化结果由于失去值的精度而被舍入?
解决方法
尝试一下:
class MyProjectTests: XCTestCase {
func testExample() throws {
let stringValue = "52048.12345678911111"
let decimalValue = Decimal(string: stringValue)!
let formatted = NumberFormatter.relativeVelocityFormatter(maxFractionDigits: decimalValue.significantFractionalDecimalDigits).string(from: decimalValue as NSNumber)
XCTAssert(formatted == stringValue)
}
}
extension NumberFormatter {
static func relativeVelocityFormatter(maxFractionDigits: Int) -> NumberFormatter {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = maxFractionDigits
formatter.numberStyle = .none
formatter.usesGroupingSeparator = true
return formatter
}
}
extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent,0)
}
}
无论如何,总会有一个限制:
33个十进制数字。