From the open-source project github.com/Flight-School/Money currencies are declared like the following:
Currency.swift
public protocol CurrencyType {
/// The three letter ISO 4217 currency code.
static var code: String { get }
/// The name of the currency.
static var name: String { get }
static var minorUnit: Int { get }
}
public enum EUR: CurrencyType {
public static var code: String {
return "EUR"
}
public static var name: String {
return "Euro"
}
public static var minorUnit: Int {
return 2
}
}
public enum GBP: CurrencyType {
public static var code: String {
return "GBP"
}
public static var name: String {
return "Pound Sterling"
}
public static var minorUnit: Int {
return 2
}
}
public enum USD: CurrencyType {
public static var code: String {
return "USD"
}
public static var name: String {
return "US Dollar"
}
public static var minorUnit: Int {
return 2
}
}
// ^^^More than 150 more like this in the file...
And a Money struct in Money.swift like so:
public struct Money<Currency: CurrencyType>: Equatable, Hashable {
/// The amount of money.
public var amount: Decimal
/// Creates an amount of money with a given decimal number.
public init(_ amount: Decimal) {
self.amount = amount
}
/// The currency type.
public var currency: CurrencyType.Type {
return Currency.self
}
/**
A monetary amount rounded to
the number of places of the minor currency unit.
*/
public var rounded: Money<Currency> {
return Money<Currency>(amount.rounded(for: Currency.self))
}
}
// MARK: - Comparable
extension Money: Comparable {
public static func < (lhs: Money<Currency>, rhs: Money<Currency>) -> Bool {
return lhs.amount < rhs.amount
}
}
Usage example:
let amount = Decimal(12)
let monetaryAmount = Money<USD>(amount) // Works with any hard-coded currency code
My Problematic Code:
What I'm trying to achieve is to construct a money object with the CurrencyType of the choice of user via their string input:
let userCurrencyCodeInput = "USD"
let userAmountInput = 39.95
let currency = CurrencyType(code: userCurrencyCodeInput ) // 'CurrencyType' cannot be constructed because it has no accessible initializers
let priceForUser = Money<currency>(userAmountInput) // Use of undeclared type 'currency'
I know that in order to get the corresponding CurrencyType enum item, I could use switch case statement but there are more than 150 currencies defined the above way (which means I would have to write more than 150 cases statically), so if there is a dynamic way of mapping a string code to the code property of the enumeration item and accessing it, I'd better learn and use it, otherwise just drop the entire library and start over the implementation in a more generic way.