Skip to content

Commit

Permalink
Merge pull request #120 from afterpay/currency-formatting
Browse files Browse the repository at this point in the history
Ensure currency formatting matches expected currency formatting
  • Loading branch information
adamjcampbell authored Oct 18, 2020
2 parents 1f97eea + 34afe62 commit 0f0db4d
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 77 deletions.
12 changes: 8 additions & 4 deletions Afterpay.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
15F7DDB725393BD30011EC25 /* CurrencyFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F7DDB625393BD30011EC25 /* CurrencyFormatter.swift */; };
6602EF0F25358A8000A0468C /* ColorScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6602EF0E25358A8000A0468C /* ColorScheme.swift */; };
6605666324E5199500DA588E /* Locales.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6605666224E5199500DA588E /* Locales.swift */; };
6615F99B24D14620005036F1 /* SVG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6615F99A24D14620005036F1 /* SVG.swift */; };
Expand All @@ -21,8 +22,8 @@
6672982A25357D80001D1C5A /* SVGConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6672982925357D80001D1C5A /* SVGConfiguration.swift */; };
667AD3542497121200BF94E5 /* CheckoutWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667AD3532497121100BF94E5 /* CheckoutWebViewController.swift */; };
6689536C24C96CB5005090B4 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6689536B24C96CB5005090B4 /* Configuration.swift */; };
66C3F7FB25397A810086DD0A /* CurrencyFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C3F7FA25397A810086DD0A /* CurrencyFormatterTests.swift */; };
66D685B224BD3FB900C7287C /* SwiftUIWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66D685B124BD3FB900C7287C /* SwiftUIWrapper.swift */; };
66DAAC8924E0CE7700127460 /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DAAC8824E0CE7700127460 /* Currency.swift */; };
66DAAC8B24E0CF0100127460 /* PriceBreakdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DAAC8A24E0CF0100127460 /* PriceBreakdown.swift */; };
66DAAC8D24E109D200127460 /* PriceBreakdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66DAAC8C24E109D200127460 /* PriceBreakdownTests.swift */; };
66E255AE24E3C14600C81F20 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E255AD24E3C14600C81F20 /* Strings.swift */; };
Expand All @@ -43,6 +44,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
15F7DDB625393BD30011EC25 /* CurrencyFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyFormatter.swift; sourceTree = "<group>"; };
6602EF0E25358A8000A0468C /* ColorScheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorScheme.swift; sourceTree = "<group>"; };
6605666224E5199500DA588E /* Locales.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locales.swift; sourceTree = "<group>"; };
6615F99A24D14620005036F1 /* SVG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SVG.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -70,8 +72,8 @@
66B57E55248F5C7D0020C642 /* Project-Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Project-Shared.xcconfig"; sourceTree = "<group>"; };
66B57E56248F5C7D0020C642 /* Project-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Project-Release.xcconfig"; sourceTree = "<group>"; };
66B57E57248F5C7D0020C642 /* Afterpay-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "Afterpay-Release.xcconfig"; sourceTree = "<group>"; };
66C3F7FA25397A810086DD0A /* CurrencyFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyFormatterTests.swift; sourceTree = "<group>"; };
66D685B124BD3FB900C7287C /* SwiftUIWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWrapper.swift; sourceTree = "<group>"; };
66DAAC8824E0CE7700127460 /* Currency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Currency.swift; sourceTree = "<group>"; };
66DAAC8A24E0CF0100127460 /* PriceBreakdown.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceBreakdown.swift; sourceTree = "<group>"; };
66DAAC8C24E109D200127460 /* PriceBreakdownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceBreakdownTests.swift; sourceTree = "<group>"; };
66E255AD24E3C14600C81F20 /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -142,6 +144,7 @@
isa = PBXGroup;
children = (
6635B95E24CAA9F000EBB3A6 /* ConfigurationTests.swift */,
66C3F7FA25397A810086DD0A /* CurrencyFormatterTests.swift */,
665FC57B2488766C00A5A93E /* Info.plist */,
66DAAC8C24E109D200127460 /* PriceBreakdownTests.swift */,
);
Expand Down Expand Up @@ -209,7 +212,7 @@
isa = PBXGroup;
children = (
6689536B24C96CB5005090B4 /* Configuration.swift */,
66DAAC8824E0CE7700127460 /* Currency.swift */,
15F7DDB625393BD30011EC25 /* CurrencyFormatter.swift */,
6605666224E5199500DA588E /* Locales.swift */,
66DAAC8A24E0CF0100127460 /* PriceBreakdown.swift */,
);
Expand Down Expand Up @@ -418,6 +421,7 @@
files = (
6635B95F24CAA9F000EBB3A6 /* ConfigurationTests.swift in Sources */,
66DAAC8D24E109D200127460 /* PriceBreakdownTests.swift in Sources */,
66C3F7FB25397A810086DD0A /* CurrencyFormatterTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -432,6 +436,7 @@
6602EF0F25358A8000A0468C /* ColorScheme.swift in Sources */,
6672982A25357D80001D1C5A /* SVGConfiguration.swift in Sources */,
6689536C24C96CB5005090B4 /* Configuration.swift in Sources */,
15F7DDB725393BD30011EC25 /* CurrencyFormatter.swift in Sources */,
66DAAC8B24E0CF0100127460 /* PriceBreakdown.swift in Sources */,
662A3AED24A999A500EFD826 /* CheckoutResult.swift in Sources */,
667AD3542497121200BF94E5 /* CheckoutWebViewController.swift in Sources */,
Expand All @@ -440,7 +445,6 @@
66D685B224BD3FB900C7287C /* SwiftUIWrapper.swift in Sources */,
666D334C24A48F5C00FCD464 /* ObjcWrapper.swift in Sources */,
946388FE24DD077F00A1227A /* InfoWebViewController.swift in Sources */,
66DAAC8924E0CE7700127460 /* Currency.swift in Sources */,
66EE9BD724DCEC3E00A81C19 /* LinkTextView.swift in Sources */,
6605666324E5199500DA588E /* Locales.swift in Sources */,
);
Expand Down
104 changes: 104 additions & 0 deletions AfterpayTests/CurrencyFormatterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// CurrencyFormatterTests.swift
// AfterpayTests
//
// Created by Adam Campbell on 16/10/20.
// Copyright © 2020 Afterpay. All rights reserved.
//

@testable import Afterpay
import XCTest

class CurrencyFormatterTests: XCTestCase {

func testAustraliaLocale() {
let formatter: (String?) -> CurrencyFormatter = { currencyCode in
CurrencyFormatter(locale: Locales.australia, currencyCode: currencyCode!)
}

let audFormatter = formatter(Locales.australia.currencyCode)
let cadFormatter = formatter(Locales.canada.currencyCode)
let gbpFormatter = formatter(Locales.greatBritain.currencyCode)
let nzdFormatter = formatter(Locales.newZealand.currencyCode)
let usdFormatter = formatter(Locales.unitedStates.currencyCode)

XCTAssertEqual(audFormatter.string(from: 120), "$120.00")
XCTAssertEqual(cadFormatter.string(from: 120), "$120.00 CAD")
XCTAssertEqual(gbpFormatter.string(from: 120), "£120.00")
XCTAssertEqual(nzdFormatter.string(from: 120), "$120.00 NZD")
XCTAssertEqual(usdFormatter.string(from: 120), "$120.00 USD")
}

func testCanadaLocale() {
let formatter: (String?) -> CurrencyFormatter = { currencyCode in
CurrencyFormatter(locale: Locales.canada, currencyCode: currencyCode!)
}

let audFormatter = formatter(Locales.australia.currencyCode)
let cadFormatter = formatter(Locales.canada.currencyCode)
let gbpFormatter = formatter(Locales.greatBritain.currencyCode)
let nzdFormatter = formatter(Locales.newZealand.currencyCode)
let usdFormatter = formatter(Locales.unitedStates.currencyCode)

XCTAssertEqual(audFormatter.string(from: 120), "$120.00 AUD")
XCTAssertEqual(cadFormatter.string(from: 120), "$120.00")
XCTAssertEqual(gbpFormatter.string(from: 120), "£120.00")
XCTAssertEqual(nzdFormatter.string(from: 120), "$120.00 NZD")
XCTAssertEqual(usdFormatter.string(from: 120), "$120.00 USD")
}

func testGreatBritainLocale() {
let formatter: (String?) -> CurrencyFormatter = { currencyCode in
CurrencyFormatter(locale: Locales.greatBritain, currencyCode: currencyCode!)
}

let audFormatter = formatter(Locales.australia.currencyCode)
let cadFormatter = formatter(Locales.canada.currencyCode)
let gbpFormatter = formatter(Locales.greatBritain.currencyCode)
let nzdFormatter = formatter(Locales.newZealand.currencyCode)
let usdFormatter = formatter(Locales.unitedStates.currencyCode)

XCTAssertEqual(audFormatter.string(from: 120), "$120.00 AUD")
XCTAssertEqual(cadFormatter.string(from: 120), "$120.00 CAD")
XCTAssertEqual(gbpFormatter.string(from: 120), "£120.00")
XCTAssertEqual(nzdFormatter.string(from: 120), "$120.00 NZD")
XCTAssertEqual(usdFormatter.string(from: 120), "$120.00 USD")
}

func testNewZealandLocale() {
let formatter: (String?) -> CurrencyFormatter = { currencyCode in
CurrencyFormatter(locale: Locales.newZealand, currencyCode: currencyCode!)
}

let audFormatter = formatter(Locales.australia.currencyCode)
let cadFormatter = formatter(Locales.canada.currencyCode)
let gbpFormatter = formatter(Locales.greatBritain.currencyCode)
let nzdFormatter = formatter(Locales.newZealand.currencyCode)
let usdFormatter = formatter(Locales.unitedStates.currencyCode)

XCTAssertEqual(audFormatter.string(from: 120), "$120.00 AUD")
XCTAssertEqual(cadFormatter.string(from: 120), "$120.00 CAD")
XCTAssertEqual(gbpFormatter.string(from: 120), "£120.00")
XCTAssertEqual(nzdFormatter.string(from: 120), "$120.00")
XCTAssertEqual(usdFormatter.string(from: 120), "$120.00 USD")
}

func testUnitedStatesLocale() {
let formatter: (String?) -> CurrencyFormatter = { currencyCode in
CurrencyFormatter(locale: Locales.unitedStates, currencyCode: currencyCode!)
}

let audFormatter = formatter(Locales.australia.currencyCode)
let cadFormatter = formatter(Locales.canada.currencyCode)
let gbpFormatter = formatter(Locales.greatBritain.currencyCode)
let nzdFormatter = formatter(Locales.newZealand.currencyCode)
let usdFormatter = formatter(Locales.unitedStates.currencyCode)

XCTAssertEqual(audFormatter.string(from: 120), "A$120.00")
XCTAssertEqual(cadFormatter.string(from: 120), "CA$120.00")
XCTAssertEqual(gbpFormatter.string(from: 120), "£120.00")
XCTAssertEqual(nzdFormatter.string(from: 120), "NZ$120.00")
XCTAssertEqual(usdFormatter.string(from: 120), "$120.00")
}

}
10 changes: 5 additions & 5 deletions Sources/Afterpay/Model/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct Configuration {

let minimumAmount: Decimal?
let maximumAmount: Decimal
let currency: Currency
let currencyCode: String
let locale: Locale

/// Creates a new configuration by taking in a minimum and maximum amount as well as a currency
Expand Down Expand Up @@ -71,8 +71,8 @@ public struct Configuration {
/// rounded to 2 decimal places. However values convertible to a Swift Decimal are accepted.
/// - maximumAmount: A amount string representation of a decimal number, rounded to 2
/// decimal places. However values convertible to a Swift Decimal are accepted.
/// - currencyCode: The currency in ISO 4217 format. Supported values include "AUD", "NZD",
/// "USD", and "CAD". However values recognized by Foundation are accepted.
/// - currencyCode: The currency in ISO 4217 format. Supported values include "AUD", "CAD",
/// "GBP", "NZD" and "USD".
/// - locale: The locale required for display of the appropriate terms and conditions and used
/// for formatting of currency. For example if the locale is set to en_AU are specified as USD.
/// More examples are available in the currency tab of the js sandbox:
Expand Down Expand Up @@ -101,7 +101,7 @@ public struct Configuration {
throw ConfigurationError.invalidOrdering(minimum: minimumAmount!, maximum: maximumAmount)
}

guard let currency = Currency(currencyCode: currencyCode) else {
guard Locales.validSet.map(\.currencyCode).contains(currencyCode) else {
throw ConfigurationError.invalidCurrencyCode(currencyCode)
}

Expand All @@ -111,7 +111,7 @@ public struct Configuration {

self.minimumAmount = minimumDecimal
self.maximumAmount = maximumDecimal
self.currency = currency
self.currencyCode = currencyCode
self.locale = locale
}

Expand Down
53 changes: 0 additions & 53 deletions Sources/Afterpay/Model/Currency.swift

This file was deleted.

38 changes: 38 additions & 0 deletions Sources/Afterpay/Model/CurrencyFormatter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CurrencyFormatter.swift
// Afterpay
//
// Created by Adam Campbell on 16/10/20.
// Copyright © 2020 Afterpay. All rights reserved.
//

import Foundation

private let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

struct CurrencyFormatter {

let locale: Locale
let currencyCode: String

func string(from decimal: Decimal) -> String? {
if locale == Locales.unitedStates || locale.currencyCode == currencyCode {
formatter.locale = locale
formatter.currencyCode = currencyCode
return formatter.string(from: decimal as NSDecimalNumber)
} else {
let currencyLocale = Locales.validSet.first { $0.currencyCode == currencyCode }
formatter.locale = currencyLocale
formatter.currencyCode = currencyCode
let formattedString = formatter.string(from: decimal as NSDecimalNumber)
return currencyLocale?.currencySymbol == Locales.unitedStates.currencySymbol
? formattedString?.appending(" \(currencyCode)")
: formattedString
}
}

}
12 changes: 3 additions & 9 deletions Sources/Afterpay/Model/PriceBreakdown.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@

import Foundation

private let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()

struct PriceBreakdown {

enum BadgePlacement: Equatable {
Expand All @@ -26,10 +20,10 @@ struct PriceBreakdown {

init(totalAmount: Decimal) {
let configuration = getConfiguration()
let formatter = configuration
.map { CurrencyFormatter(locale: $0.locale, currencyCode: $0.currencyCode) }
let format = { formatter?.string(from: $0) }

formatter.currencySymbol = configuration?.currency.symbol

let format: (Decimal) -> String? = { formatter.string(from: $0 as NSDecimalNumber) }
let formattedMinimum = configuration?.minimumAmount.flatMap(format)
let formattedMaximum = (configuration?.maximumAmount).flatMap(format)
let formattedPayment = format(totalAmount / 4)
Expand Down
8 changes: 2 additions & 6 deletions Sources/Afterpay/Views/SVGView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ import UIKit

final class SVGView: Macaw.SVGView {

var svg: SVG {
svgConfiguration.svg(localizedFor: locale, withTraits: traitCollection)
}
var svg: SVG { svgConfiguration.svg(localizedFor: locale, withTraits: traitCollection) }

var svgConfiguration: SVGConfiguration {
didSet { svgDidChange() }
}

private var locale: Locale {
getConfiguration()?.locale ?? Locales.unitedStates
}
private var locale: Locale { getConfiguration()?.locale ?? Locales.unitedStates }

init(svgConfiguration: SVGConfiguration) {
self.svgConfiguration = svgConfiguration
Expand Down

0 comments on commit 0f0db4d

Please sign in to comment.