├── .ci ├── buildkite │ ├── pipeline.template.yml │ └── upload ├── scripts │ ├── send-coverage │ ├── test-ios │ ├── test-osx │ └── update-bundler └── xcodebuild-data │ └── .gitkeep ├── .fastlane ├── Appfile └── Fastfile ├── .gitignore ├── .jazzy.yml ├── .slather.yml ├── .swift-version ├── CHANGELOG.md ├── Cartfile ├── Cartfile.resolved ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Money.podspec ├── Money.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Money.xcscheme ├── README.md ├── Sources ├── ApplePay.swift ├── Autogenerated.swift ├── Bitcoin.swift ├── Currency.swift ├── Decimal.swift ├── DecimalNumberType.swift ├── Locale.swift ├── Money.swift ├── NSDecimalExtensions.swift └── NSDecimalNumberExtensions.swift ├── Supporting Files ├── Carthage.xcconfig ├── Generate.swift ├── Info.plist ├── Money.h ├── Money.xcconfig ├── Version.xcconfig └── Warnings.xcconfig ├── Tests ├── ApplePayTests.swift ├── AutogeneratedTests.swift ├── BitcoinTests.swift ├── DecimalNumberTypeTests.swift ├── DecimalTests.swift ├── Info.plist ├── LocaleTests.swift ├── MoneyTests.swift ├── NSDecimalNumberTests.swift └── NSDecimalTests.swift └── header.png /.ci/buildkite/pipeline.template.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - 3 | name: "macOS" 4 | command: "source /usr/local/opt/chruby/share/chruby/chruby.sh && chruby ruby && bundle install --quiet && bundle exec fastlane test_mac" 5 | agents: 6 | xcode: "$XCODE" 7 | - 8 | name: "iOS" 9 | command: "source /usr/local/opt/chruby/share/chruby/chruby.sh && chruby ruby && bundle install --quiet && bundle exec fastlane test_ios" 10 | agents: 11 | queue: "iOS-Simulator" 12 | xcode: "$XCODE" 13 | - 14 | name: "tvOS" 15 | command: "source /usr/local/opt/chruby/share/chruby/chruby.sh && chruby ruby && bundle install --quiet && bundle exec fastlane test_tvos" 16 | agents: 17 | queue: "iOS-Simulator" 18 | xcode: "$XCODE" 19 | -------------------------------------------------------------------------------- /.ci/buildkite/upload: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | # Makes sure all the steps run on this same agent 6 | sed "s/\$XCODE/$BUILDKITE_AGENT_META_DATA_XCODE/;s/\$COMMIT/$BUILDKITE_COMMIT/" .ci/buildkite/pipeline.template.yml 7 | -------------------------------------------------------------------------------- /.ci/scripts/send-coverage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source /usr/local/opt/chruby/share/chruby/chruby.sh 3 | chruby ruby 4 | bundle exec slather coverage --scheme Money-iOS --buildkite --coveralls --build-directory .ci/xcodebuild-data 5 | -------------------------------------------------------------------------------- /.ci/scripts/test-ios: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source /usr/local/opt/chruby/share/chruby/chruby.sh 3 | chruby ruby 4 | bundle exec fastlane ios test 5 | 6 | -------------------------------------------------------------------------------- /.ci/scripts/test-osx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source /usr/local/opt/chruby/share/chruby/chruby.sh 3 | chruby ruby 4 | bundle exec fastlane mac test 5 | -------------------------------------------------------------------------------- /.ci/scripts/update-bundler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | source /usr/local/opt/chruby/share/chruby/chruby.sh 3 | chruby ruby 4 | bundle update && bundle exec fastlane enable_crash_reporting 5 | -------------------------------------------------------------------------------- /.ci/xcodebuild-data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danthorpe/Money/a8675ca149aa08c5978c2dab7f1aae346be5e486/.ci/xcodebuild-data/.gitkeep -------------------------------------------------------------------------------- /.fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier "me.danthorpe.Money" 2 | apple_id "someone@somewhere.com" 3 | -------------------------------------------------------------------------------- /.fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | lane :test_ios do 2 | carthage(platform: "iOS") 3 | scan(scheme: "Money", destination: "platform=iOS Simulator,OS=10.0,name=iPhone 6s") 4 | end 5 | 6 | lane :test_mac do 7 | carthage(platform: "Mac") 8 | scan(scheme: "Money", destination: "platform=macOS") 9 | end 10 | 11 | lane :test_tvos do 12 | carthage(platform: "tvOS") 13 | scan(scheme: "Money", destination: "platform=tvOS Simulator,OS=10.0,name=Apple TV 1080p") 14 | end 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # Carthage 21 | # 22 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 23 | Carthage/Checkouts 24 | 25 | Carthage/Build 26 | .fastlane/report.xml 27 | .ci/xcodebuild-data/* 28 | *.coverage.txt 29 | docs 30 | -------------------------------------------------------------------------------- /.jazzy.yml: -------------------------------------------------------------------------------- 1 | author: Daniel Thorpe 2 | author_url: http://danthorpe.me 3 | module: Money 4 | module_version: 2.0.0 5 | github_url: https://github.com/danthorpe/Money 6 | readme: README.md 7 | podspec: Money.podspec 8 | 9 | swift_version: 3.0 10 | xcodebuild-arguments: -scheme,Money 11 | 12 | custom_categories: 13 | - name: Decimals 14 | children: 15 | - DecimalNumberType 16 | - DecimalNumberBehaviorType 17 | - DecimalNumberBehavior 18 | - _Decimal 19 | - Decimal 20 | - BankersDecimal 21 | - _DecimalCoder 22 | 23 | - name: Currency 24 | children: 25 | - CurrencyType 26 | - CustomCurrencyType 27 | - ISOCurrencyType 28 | - CryptoCurrencyType 29 | - Currency 30 | 31 | - name: Money 32 | children: 33 | - MoneyType 34 | - _Money 35 | - _MoneyCoder 36 | - Money 37 | 38 | - name: Bitcoin 39 | children: 40 | - BitcoinCurrencyType 41 | - BTC 42 | - XBT 43 | 44 | - name: Pay 45 | children: 46 | - PaymentSummaryItemType 47 | - PaymentSummaryItem 48 | - PaymentSummaryItemCoder 49 | - PKPaymentRequest 50 | 51 | - name: Locale 52 | children: 53 | - LocaleType 54 | - CountryType 55 | - LanguageType 56 | - NSLocale 57 | - Locale 58 | - AfrikaansSpeakingCountry 59 | - AlbanianSpeakingCountry 60 | - ArabicSpeakingCountry 61 | - BengaliSpeakingCountry 62 | - CatalanSpeakingCountry 63 | - CentralKurdishSpeakingCountry 64 | - ChineseSpeakingCountry 65 | - CroatianSpeakingCountry 66 | - DanishSpeakingCountry 67 | - DutchSpeakingCountry 68 | - EnglishSpeakingCountry 69 | - EweSpeakingCountry 70 | - FrenchSpeakingCountry 71 | - FulahSpeakingCountry 72 | - GermanSpeakingCountry 73 | - GreekSpeakingCountry 74 | - HausaSpeakingCountry 75 | - ItalianSpeakingCountry 76 | - KoreanSpeakingCountry 77 | - LingalaSpeakingCountry 78 | - MalaySpeakingCountry 79 | - MasaiSpeakingCountry 80 | - NepaliSpeakingCountry 81 | - NorthernSamiSpeakingCountry 82 | - NorwegianBokmålSpeakingCountry 83 | - OromoSpeakingCountry 84 | - OsseticSpeakingCountry 85 | - PersianSpeakingCountry 86 | - PortugueseSpeakingCountry 87 | - PunjabiSpeakingCountry 88 | - QuechuaSpeakingCountry 89 | - RomanianSpeakingCountry 90 | - RussianSpeakingCountry 91 | - SerbianSpeakingCountry 92 | - SomaliSpeakingCountry 93 | - SpanishSpeakingCountry 94 | - SwahiliSpeakingCountry 95 | - SwedishSpeakingCountry 96 | - SwissGermanSpeakingCountry 97 | - TamilSpeakingCountry 98 | - TesoSpeakingCountry 99 | - TibetanSpeakingCountry 100 | - TigrinyaSpeakingCountry 101 | - TurkishSpeakingCountry 102 | - UrduSpeakingCountry 103 | - UzbekSpeakingCountry 104 | - YorubaSpeakingCountry 105 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | coverage_service: coveralls 2 | xcodeproj: Money.xcodeproj 3 | build_directory: .ci/xcodebuild-data 4 | ignore: 5 | - Tests/* 6 | - Supporting Files/* -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.0.0 2 | 1. [[MNY-63](https://github.com/danthorpe/Money/pull/63)]: Updates to Swift 3.0. 3 | 4 | # 1.8.0 5 | 1. [[MNY-61](https://github.com/danthorpe/Money/pull/61)]: Updates to Swift 2.3. 6 | 7 | # 1.7.0 8 | 1. [[MNY-47](https://github.com/danthorpe/Money/pull/47)]: Updates to Swift 2.2. 9 | 10 | # 1.6.2 11 | 1. [[MNY-45](https://github.com/danthorpe/Money/pull/45)]: Fixes a serious bug where `Money` did not have the correct currency scale. 12 | 13 | # 1.6.1 14 | 1. [[MNY-42](https://github.com/danthorpe/Money/pull/42)]: Removes all the example projects to [danthorpe/Examples](https://github.com/danthorpe/Examples). This is done mostly to avoid an issue where Carthage attempts to build all Xcode projects it can find in a repository. 15 | 2. [[MNY-43](https://github.com/danthorpe/Money/pull/43)]: Switch the code coverage reporting tool to [Coveralls](https://coveralls.io/github/danthorpe/Money), and got the coverage back to 100%. 16 | 17 | # 1.6.0 18 | 1. [[MNY-39](https://github.com/danthorpe/Money/pull/39)]: Updates the spelling of CocoaPods, thanks [https://github.com/ReadmeCritic](@ReadmeCritic)! 19 | 2. [[MNY-34](https://github.com/danthorpe/Money/pull/34)]: Refactors `CurrencyType` to remove the formatter property, and include a default `CurrencyStyle`. This update makes it a lot easier to make custom currency types, and overall improves or fixes bugs with the ISO currency types. If you have custom currencies you may need to perform some slight refactoring, but it shouldn’t be too complex. 20 | 21 | # 1.5.1 22 | Cleans up a few issues related to updating to the latest Carthage, and the example project. 23 | 24 | # 1.5.0 25 | 1. [[MNY-38](https://github.com/danthorpe/Money/pull/38)]: Removes the FX functionality from this framework. 26 | 27 | # 1.4.2 28 | 1. [[MNY-39](https://github.com/danthorpe/Money/pull/30)]: Updates to the latest version of [Result](https://github.com/antitypical/Result/releases/tag/1.0.1) dependency. Thanks to [@mrdavey](https://github.com/mrdavey) and [@hsoi](https://github.com/hsoi) for spotting this! 29 | 30 | # 1.4.1 31 | 1. [[MNY-28](https://github.com/danthorpe/Money/pull/28)]: Documentation is now hosted at [docs.danthorpe.me](http://docs.danthorpe.me/money/1.4.1/). 32 | 33 | # 1.4.0 34 | 1. [[MNY-25](https://github.com/danthorpe/Money/pull/25)]: Adds convenience initializers to `DecimalNumberType` for `Int` (including all the variants) and `Double` parameters. Although not technically needed, (as it’s integer and float convertible) this makes it a lot easier to construct `Money` types. 35 | 2. [[MNY-24](https://github.com/danthorpe/Money/pull/26)]: Refactors the localized string formatting APIs. Fixed a few bugs which may have rendered string incorrectly formatted in some locales. Also added the ability to format `MoneyType` values using specific language and country locales. See the README for more info. 36 | 3. [[MNY-27](https://github.com/danthorpe/Money/pull/27)]: Finally got Jazzy to generate the documentation, and have created a config for the project. Hopefully now CocoaDocs will correctly parse the framework and generate docs. Documentation coverage is 96%. 37 | 38 | # 1.3.0 39 | 1. [[MNY-21](https://github.com/danthorpe/Money/pull/21), [MNY-22](https://github.com/danthorpe/Money/pull/22)]: Adds support for initializing `MoneyType`s with minor units. Thanks to [@jlalvarez18](https://github.com/jlalvarez18). 40 | 2. [[MNY-23](https://github.com/danthorpe/Money/pull/23)]: Adds some convenience extensions to create  pay payment requests using an array of `PaymentSummaryItem` which is a new type generic over `MoneyType`. This is only available on iOS, and it allows consumers to use `Money` directly with  pay APIs. 41 | 42 | # 1.2.1 43 | 1. [[MNY-19](https://github.com/danthorpe/Money/pull/19)]: Fixes a bunch of miscellaneous spelling mistakes in the README and code documentation. 44 | 2. [[MNY-20](https://github.com/danthorpe/Money/pull/20)]: Fixes a mistake where DVR which is only needed for the test suit was not in a private Cartfile. Also switches to the official @venmo DVR after recent merges in their project. Thanks to @dasmer for this. 45 | 46 | # 1.2.0 47 | 1. [[MNY-18](https://github.com/danthorpe/Money/pull/18)]: Adds Bitcoin currency types and support for % commission with FX. 48 | * Creates `BTC` and `XBT` types. 49 | * Refactors `FXQuote` into a struct (no longer subclass-able) but with a percentage commission property. Commission defaults to 0%. 50 | * FX method `quote`, now returns `FXTransaction` as the value of the `Result`. This new value type composes the original base money, commission (in the same base money currency), the exchange rate, and the counter money. The type supports `ValueCoding`. 51 | * A new FX provider, CEX.IO get support for buying and selling bitcoin using `USD`, `EUR` and `RUB`. 52 | 53 | # 1.1.0 54 | 1. [[MNY-16](https://github.com/danthorpe/Money/pull/16)]: Grab bag of minor issues post 1.0 release. 55 | * Cleans up some minor mistakes (spelling etc). 56 | * Adds `NSCoding` conformance to `FXQuote` - so it can be persisted if needed. 57 | * Adds `FXRemoteProviderType.quote(: BaseMoney, completion: Result<(BaseMoney, FXQuote, CounterMoney), FXError> -> Void) -> NSURLSessionDataTask` API. This is the nuts and bolts of the FX provider now. It returns as its result, the base money (i.e. the input), the quote (which includes the rate), and the counter money (i.e. the output). The `fx` method still exists, and it just unwraps the tuple to return the counter money. See the updated README. 58 | 2. [[MNY-17](https://github.com/danthorpe/Money/pull/17)]: There was an oversight in the functions in `DecimalNumberType` which accepts `NSDecimalNumberBehaviors` as an argument. These were unnecessary so I’ve removed them. Hence the minor version bump. 59 | 60 | # 1.0.0 61 | 🎉🐝 Initial release of Money. 62 | 63 | - [x] DecimalNumberType with full support for mathematics operators 64 | - [x] Strongly typed ISO currencies 65 | - [x] Strongly typed ISO money type which conforms to DecimalNumberType 66 | - [x] Generic Foreign Exchange APIs 67 | - [x] Yahoo FX provider 68 | - [x] OpenExchangeRates.org FX provider 69 | - [x] 100% of code covered by tests 70 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "danthorpe/ValueCoding" "2.1.0" 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "danthorpe/ValueCoding" "2.1.0" 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'scan' 4 | gem 'fastlane' 5 | gem 'xcpretty' 6 | gem 'slather' 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.7.1) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | addressable (2.4.0) 11 | babosa (1.0.2) 12 | cert (1.4.3) 13 | fastlane_core (>= 0.52.1, < 1.0.0) 14 | spaceship (>= 0.34.2, < 1.0.0) 15 | claide (1.0.1) 16 | clamp (0.6.5) 17 | colored (1.2) 18 | commander (4.4.0) 19 | highline (~> 1.7.2) 20 | credentials_manager (0.16.2) 21 | colored 22 | commander (>= 4.3.5) 23 | highline (>= 1.7.1) 24 | security 25 | deliver (1.14.2) 26 | credentials_manager (>= 0.16.1, < 1.0.0) 27 | fastimage (~> 1.6) 28 | fastlane_core (>= 0.52.1, < 1.0.0) 29 | plist (>= 3.1.0, < 4.0.0) 30 | spaceship (>= 0.35.0, < 1.0.0) 31 | domain_name (0.5.20160826) 32 | unf (>= 0.0.5, < 1.0.0) 33 | dotenv (2.1.1) 34 | excon (0.53.0) 35 | faraday (0.9.2) 36 | multipart-post (>= 1.2, < 3) 37 | faraday-cookie_jar (0.0.6) 38 | faraday (>= 0.7.4) 39 | http-cookie (~> 1.0.0) 40 | faraday_middleware (0.10.0) 41 | faraday (>= 0.7.4, < 0.10) 42 | fastimage (1.6.8) 43 | addressable (~> 2.3, >= 2.3.5) 44 | fastlane (1.105.2) 45 | activesupport (< 5) 46 | addressable (>= 2.3, < 3.0.0) 47 | bundler (~> 1.12) 48 | cert (>= 1.4.3, < 2.0.0) 49 | credentials_manager (>= 0.16.1, < 1.0.0) 50 | deliver (>= 1.14.2, < 2.0.0) 51 | fastlane_core (>= 0.52.1, < 1.0.0) 52 | frameit (>= 2.8.0, < 3.0.0) 53 | gym (>= 1.11.0, < 2.0.0) 54 | krausefx-shenzhen (>= 0.14.10, < 1.0.0) 55 | match (>= 0.8.1, < 1.0.0) 56 | multipart-post (~> 2.0.0) 57 | pem (>= 1.3.2, < 2.0.0) 58 | pilot (>= 1.10.1, < 2.0.0) 59 | plist (>= 3.1.0, < 4.0.0) 60 | produce (>= 1.2.1, < 2.0.0) 61 | scan (>= 0.13.1, < 1.0.0) 62 | screengrab (>= 0.5.2, < 1.0.0) 63 | sigh (>= 1.11.2, < 2.0.0) 64 | slack-notifier (>= 1.3, < 2.0.0) 65 | snapshot (>= 1.16.2, < 2.0.0) 66 | spaceship (>= 0.35.0, < 1.0.0) 67 | supply (>= 0.7.1, < 1.0.0) 68 | terminal-notifier (>= 1.6.2, < 2.0.0) 69 | terminal-table (>= 1.4.5, < 2.0.0) 70 | word_wrap (~> 1.0.0) 71 | xcode-install (~> 2.0.0) 72 | xcodeproj (>= 0.20, < 2.0.0) 73 | xcpretty (>= 0.2.3) 74 | fastlane_core (0.52.1) 75 | babosa 76 | colored 77 | commander (>= 4.4.0, <= 5.0.0) 78 | credentials_manager (>= 0.16.1, < 1.0.0) 79 | excon (>= 0.45.0, < 1.0) 80 | gh_inspector (>= 1.0.1, < 2.0.0) 81 | highline (>= 1.7.2) 82 | json 83 | multi_json 84 | plist (~> 3.1) 85 | rubyzip (~> 1.1.6) 86 | terminal-table (~> 1.4.5) 87 | frameit (2.8.0) 88 | deliver (> 0.3) 89 | fastimage (~> 1.6.3) 90 | fastlane_core (>= 0.52.1, < 1.0.0) 91 | mini_magick (~> 4.5.1) 92 | gh_inspector (1.0.2) 93 | google-api-client (0.9.15) 94 | addressable (~> 2.3) 95 | googleauth (~> 0.5) 96 | httpclient (~> 2.7) 97 | hurley (~> 0.1) 98 | memoist (~> 0.11) 99 | mime-types (>= 1.6) 100 | representable (~> 2.3.0) 101 | retriable (~> 2.0) 102 | googleauth (0.5.1) 103 | faraday (~> 0.9) 104 | jwt (~> 1.4) 105 | logging (~> 2.0) 106 | memoist (~> 0.12) 107 | multi_json (~> 1.11) 108 | os (~> 0.9) 109 | signet (~> 0.7) 110 | gym (1.11.2) 111 | fastlane_core (>= 0.52.1, < 1.0.0) 112 | plist (>= 3.1.0, < 4.0.0) 113 | rubyzip (>= 1.1.7) 114 | terminal-table (>= 1.4.5, < 2.0.0) 115 | xcpretty (>= 0.2.4, < 1.0.0) 116 | highline (1.7.8) 117 | http-cookie (1.0.3) 118 | domain_name (~> 0.5) 119 | httpclient (2.8.2.4) 120 | hurley (0.2) 121 | i18n (0.7.0) 122 | json (1.8.3) 123 | jwt (1.5.6) 124 | krausefx-shenzhen (0.14.10) 125 | commander (>= 4.3, < 5.0) 126 | dotenv (>= 0.7) 127 | faraday (~> 0.9) 128 | faraday_middleware (~> 0.9) 129 | highline (>= 1.7.2) 130 | json (~> 1.8) 131 | net-sftp (~> 2.1.2) 132 | plist (~> 3.1.0) 133 | rubyzip (~> 1.1) 134 | security (~> 0.1.3) 135 | terminal-table (~> 1.4.5) 136 | little-plugger (1.1.4) 137 | logging (2.1.0) 138 | little-plugger (~> 1.1) 139 | multi_json (~> 1.10) 140 | match (0.8.1) 141 | cert (>= 1.4.3, < 2.0.0) 142 | credentials_manager (>= 0.16.1, < 1.0.0) 143 | fastlane_core (>= 0.52.1, < 1.0.0) 144 | security 145 | sigh (>= 1.11.2, < 2.0.0) 146 | spaceship (>= 0.34.2, < 1.0.0) 147 | memoist (0.15.0) 148 | mime-types (3.1) 149 | mime-types-data (~> 3.2015) 150 | mime-types-data (3.2016.0521) 151 | mini_magick (4.5.1) 152 | mini_portile2 (2.1.0) 153 | minitest (5.9.1) 154 | multi_json (1.12.1) 155 | multi_xml (0.5.5) 156 | multipart-post (2.0.0) 157 | net-sftp (2.1.2) 158 | net-ssh (>= 2.6.5) 159 | net-ssh (3.2.0) 160 | nokogiri (1.6.8.1) 161 | mini_portile2 (~> 2.1.0) 162 | os (0.9.6) 163 | pem (1.3.2) 164 | fastlane_core (>= 0.43.1, < 1.0.0) 165 | spaceship (>= 0.26.2, < 1.0.0) 166 | pilot (1.10.1) 167 | credentials_manager (>= 0.16.0) 168 | fastlane_core (>= 0.52.1, < 1.0.0) 169 | spaceship (>= 0.34.2, < 1.0.0) 170 | terminal-table (~> 1.4.5) 171 | plist (3.1.0) 172 | produce (1.2.1) 173 | fastlane_core (>= 0.52.1, < 1.0.0) 174 | spaceship (>= 0.34.2, < 1.0.0) 175 | representable (2.3.0) 176 | uber (~> 0.0.7) 177 | retriable (2.1.0) 178 | rouge (1.11.1) 179 | rubyzip (1.1.7) 180 | scan (0.13.1) 181 | fastlane_core (>= 0.52.1, < 1.0.0) 182 | slack-notifier (~> 1.3) 183 | terminal-table 184 | xcpretty (>= 0.2.2) 185 | xcpretty-travis-formatter (>= 0.0.3) 186 | screengrab (0.5.5) 187 | fastlane_core (>= 0.52.1, < 1.0.0) 188 | security (0.1.3) 189 | sigh (1.11.2) 190 | fastlane_core (>= 0.52.1, < 1.0.0) 191 | plist (~> 3.1) 192 | spaceship (>= 0.34.2, < 1.0.0) 193 | signet (0.7.3) 194 | addressable (~> 2.3) 195 | faraday (~> 0.9) 196 | jwt (~> 1.5) 197 | multi_json (~> 1.10) 198 | slack-notifier (1.5.1) 199 | slather (2.3.0) 200 | activesupport (>= 4.0.2, < 5) 201 | clamp (~> 0.6) 202 | nokogiri (~> 1.6.3) 203 | xcodeproj (>= 0.20, < 2.0.0) 204 | snapshot (1.16.2) 205 | fastimage (~> 1.6.3) 206 | fastlane_core (>= 0.52.1, < 1.0.0) 207 | plist (~> 3.1.0) 208 | xcpretty (>= 0.2.3) 209 | spaceship (0.36.0) 210 | colored 211 | credentials_manager (>= 0.16.0) 212 | faraday (~> 0.9) 213 | faraday-cookie_jar (~> 0.0.6) 214 | faraday_middleware (~> 0.9) 215 | fastimage (~> 1.6) 216 | multi_xml (~> 0.5) 217 | plist (>= 3.1.0, < 4.0.0) 218 | supply (0.7.1) 219 | credentials_manager (>= 0.15.0) 220 | fastlane_core (>= 0.43.4) 221 | google-api-client (~> 0.9.1) 222 | terminal-notifier (1.7.1) 223 | terminal-table (1.4.5) 224 | thread_safe (0.3.5) 225 | tzinfo (1.2.2) 226 | thread_safe (~> 0.1) 227 | uber (0.0.15) 228 | unf (0.1.4) 229 | unf_ext 230 | unf_ext (0.0.7.2) 231 | word_wrap (1.0.0) 232 | xcode-install (2.0.7) 233 | claide (>= 0.9.1, < 1.1.0) 234 | spaceship (>= 0.25.1, < 1.0.0) 235 | xcodeproj (1.3.2) 236 | activesupport (>= 3) 237 | claide (>= 1.0.1, < 2.0) 238 | colored (~> 1.2) 239 | xcpretty (0.2.4) 240 | rouge (~> 1.8) 241 | xcpretty-travis-formatter (0.0.4) 242 | xcpretty (~> 0.2, >= 0.0.7) 243 | 244 | PLATFORMS 245 | ruby 246 | 247 | DEPENDENCIES 248 | fastlane 249 | scan 250 | slather 251 | xcpretty 252 | 253 | BUNDLED WITH 254 | 1.13.3 255 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2016 Daniel Thorpe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Money.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Money" 3 | s.version = "2.0.1" 4 | s.summary = "Swift types for working with Money." 5 | s.description = <<-DESC 6 | 7 | Money is a Swift cross platform framework for iOS, watchOS, tvOS and OS X. 8 | 9 | It provides types and functionality to help represent and manipulate money 10 | and currency related information. 11 | 12 | DESC 13 | s.homepage = "https://github.com/danthorpe/Money" 14 | s.license = 'MIT' 15 | s.author = { "Daniel Thorpe" => "@danthorpe" } 16 | s.source = { :git => "https://github.com/danthorpe/Money.git", :tag => s.version.to_s } 17 | s.module_name = 'Money' 18 | s.social_media_url = 'https://twitter.com/danthorpe' 19 | s.requires_arc = true 20 | s.ios.deployment_target = '8.0' 21 | s.osx.deployment_target = '10.11' 22 | s.tvos.deployment_target = '9.0' 23 | s.watchos.deployment_target = '2.0' 24 | 25 | s.source_files = [ 26 | 'Sources/*.swift' 27 | ] 28 | 29 | s.osx.exclude_files = [ 'Sources/ApplePay.swift' ] 30 | s.watchos.exclude_files = [ 'Sources/ApplePay.swift' ] 31 | s.tvos.exclude_files = [ 'Sources/ApplePay.swift' ] 32 | 33 | s.dependency 'ValueCoding', '~> 2' 34 | 35 | end 36 | 37 | -------------------------------------------------------------------------------- /Money.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 65687D5B1DB3D8B70016FB31 /* Generate Swift */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 65687D5E1DB3D8B70016FB31 /* Build configuration list for PBXAggregateTarget "Generate Swift" */; 13 | buildPhases = ( 14 | 65687D5F1DB3D8C30016FB31 /* Generate Swift */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = "Generate Swift"; 19 | productName = "Generate Swift"; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 655155121DB3D50700368D35 /* ApplePay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155111DB3D50700368D35 /* ApplePay.swift */; }; 25 | 6551551F1DB3D59900368D35 /* ApplePayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155151DB3D59900368D35 /* ApplePayTests.swift */; }; 26 | 655155201DB3D59900368D35 /* AutogeneratedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155161DB3D59900368D35 /* AutogeneratedTests.swift */; }; 27 | 655155211DB3D59900368D35 /* BitcoinTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155171DB3D59900368D35 /* BitcoinTests.swift */; }; 28 | 655155221DB3D59900368D35 /* DecimalNumberTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155181DB3D59900368D35 /* DecimalNumberTypeTests.swift */; }; 29 | 655155231DB3D59900368D35 /* DecimalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655155191DB3D59900368D35 /* DecimalTests.swift */; }; 30 | 655155251DB3D59900368D35 /* LocaleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551551B1DB3D59900368D35 /* LocaleTests.swift */; }; 31 | 655155261DB3D59900368D35 /* MoneyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551551C1DB3D59900368D35 /* MoneyTests.swift */; }; 32 | 655155271DB3D59900368D35 /* NSDecimalNumberTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551551D1DB3D59900368D35 /* NSDecimalNumberTests.swift */; }; 33 | 655155281DB3D59900368D35 /* NSDecimalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551551E1DB3D59900368D35 /* NSDecimalTests.swift */; }; 34 | 65599E7D1DB3D17F00EC123C /* Money.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65599E721DB3D17E00EC123C /* Money.framework */; }; 35 | 65599E951DB3D2D500EC123C /* Autogenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E8B1DB3D2D500EC123C /* Autogenerated.swift */; }; 36 | 65599E961DB3D2D500EC123C /* Bitcoin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E8C1DB3D2D500EC123C /* Bitcoin.swift */; }; 37 | 65599E971DB3D2D500EC123C /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E8D1DB3D2D500EC123C /* Currency.swift */; }; 38 | 65599E981DB3D2D500EC123C /* Decimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E8E1DB3D2D500EC123C /* Decimal.swift */; }; 39 | 65599E991DB3D2D500EC123C /* DecimalNumberType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E8F1DB3D2D500EC123C /* DecimalNumberType.swift */; }; 40 | 65599E9A1DB3D2D500EC123C /* Locale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E901DB3D2D500EC123C /* Locale.swift */; }; 41 | 65599E9B1DB3D2D500EC123C /* Money.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E911DB3D2D500EC123C /* Money.swift */; }; 42 | 65599E9C1DB3D2D500EC123C /* NSDecimalExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E921DB3D2D500EC123C /* NSDecimalExtensions.swift */; }; 43 | 65599E9D1DB3D2D500EC123C /* NSDecimalNumberExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65599E931DB3D2D500EC123C /* NSDecimalNumberExtensions.swift */; }; 44 | 65ECB2B11DB3DC9900F96F46 /* Money.h in Headers */ = {isa = PBXBuildFile; fileRef = 65599E6A1DB3CFD300EC123C /* Money.h */; settings = {ATTRIBUTES = (Public, ); }; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXContainerItemProxy section */ 48 | 652899911DB3D96B0099D541 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 65599E611DB3CF2E00EC123C /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 65687D5B1DB3D8B70016FB31; 53 | remoteInfo = "Generate Swift"; 54 | }; 55 | 65599E7E1DB3D17F00EC123C /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = 65599E611DB3CF2E00EC123C /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = 65599E711DB3D17E00EC123C; 60 | remoteInfo = Money; 61 | }; 62 | /* End PBXContainerItemProxy section */ 63 | 64 | /* Begin PBXFileReference section */ 65 | 655155101DB3D45100368D35 /* Carthage.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Carthage.xcconfig; sourceTree = ""; }; 66 | 655155111DB3D50700368D35 /* ApplePay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplePay.swift; sourceTree = ""; }; 67 | 655155151DB3D59900368D35 /* ApplePayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplePayTests.swift; sourceTree = ""; }; 68 | 655155161DB3D59900368D35 /* AutogeneratedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutogeneratedTests.swift; sourceTree = ""; }; 69 | 655155171DB3D59900368D35 /* BitcoinTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoinTests.swift; sourceTree = ""; }; 70 | 655155181DB3D59900368D35 /* DecimalNumberTypeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecimalNumberTypeTests.swift; sourceTree = ""; }; 71 | 655155191DB3D59900368D35 /* DecimalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecimalTests.swift; sourceTree = ""; }; 72 | 6551551A1DB3D59900368D35 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | 6551551B1DB3D59900368D35 /* LocaleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocaleTests.swift; sourceTree = ""; }; 74 | 6551551C1DB3D59900368D35 /* MoneyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoneyTests.swift; sourceTree = ""; }; 75 | 6551551D1DB3D59900368D35 /* NSDecimalNumberTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDecimalNumberTests.swift; sourceTree = ""; }; 76 | 6551551E1DB3D59900368D35 /* NSDecimalTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDecimalTests.swift; sourceTree = ""; }; 77 | 65599E681DB3CFD300EC123C /* Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generate.swift; sourceTree = ""; }; 78 | 65599E691DB3CFD300EC123C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | 65599E6A1DB3CFD300EC123C /* Money.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Money.h; sourceTree = ""; }; 80 | 65599E6B1DB3CFD300EC123C /* Money.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Money.xcconfig; sourceTree = ""; }; 81 | 65599E6C1DB3CFD300EC123C /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; 82 | 65599E721DB3D17E00EC123C /* Money.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Money.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | 65599E7C1DB3D17F00EC123C /* MoneyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MoneyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | 65599E8B1DB3D2D500EC123C /* Autogenerated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Autogenerated.swift; sourceTree = ""; }; 85 | 65599E8C1DB3D2D500EC123C /* Bitcoin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bitcoin.swift; sourceTree = ""; }; 86 | 65599E8D1DB3D2D500EC123C /* Currency.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Currency.swift; sourceTree = ""; }; 87 | 65599E8E1DB3D2D500EC123C /* Decimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decimal.swift; sourceTree = ""; }; 88 | 65599E8F1DB3D2D500EC123C /* DecimalNumberType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecimalNumberType.swift; sourceTree = ""; }; 89 | 65599E901DB3D2D500EC123C /* Locale.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Locale.swift; sourceTree = ""; }; 90 | 65599E911DB3D2D500EC123C /* Money.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Money.swift; sourceTree = ""; }; 91 | 65599E921DB3D2D500EC123C /* NSDecimalExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDecimalExtensions.swift; sourceTree = ""; }; 92 | 65599E931DB3D2D500EC123C /* NSDecimalNumberExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSDecimalNumberExtensions.swift; sourceTree = ""; }; 93 | 65AA39ED1DFEF418000942A7 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 94 | 65ECB2BF1DB4134C00F96F46 /* Autogenerate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Autogenerate.h; sourceTree = ""; }; 95 | 65ECB2C01DB4134C00F96F46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 65599E6E1DB3D17E00EC123C /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 65599E791DB3D17F00EC123C /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | 65599E7D1DB3D17F00EC123C /* Money.framework in Frameworks */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXFrameworksBuildPhase section */ 115 | 116 | /* Begin PBXGroup section */ 117 | 655155131DB3D56400368D35 /* ... */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 65599E671DB3CFD300EC123C /* Supporting Files */, 121 | 65599E731DB3D17E00EC123C /* Products */, 122 | ); 123 | name = ...; 124 | sourceTree = ""; 125 | }; 126 | 655155141DB3D59900368D35 /* Tests */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 6551551A1DB3D59900368D35 /* Info.plist */, 130 | 655155151DB3D59900368D35 /* ApplePayTests.swift */, 131 | 655155161DB3D59900368D35 /* AutogeneratedTests.swift */, 132 | 655155171DB3D59900368D35 /* BitcoinTests.swift */, 133 | 655155181DB3D59900368D35 /* DecimalNumberTypeTests.swift */, 134 | 655155191DB3D59900368D35 /* DecimalTests.swift */, 135 | 6551551B1DB3D59900368D35 /* LocaleTests.swift */, 136 | 6551551C1DB3D59900368D35 /* MoneyTests.swift */, 137 | 6551551D1DB3D59900368D35 /* NSDecimalNumberTests.swift */, 138 | 6551551E1DB3D59900368D35 /* NSDecimalTests.swift */, 139 | ); 140 | path = Tests; 141 | sourceTree = ""; 142 | }; 143 | 65599E601DB3CF2E00EC123C = { 144 | isa = PBXGroup; 145 | children = ( 146 | 65599E8A1DB3D2D500EC123C /* Sources */, 147 | 655155141DB3D59900368D35 /* Tests */, 148 | 655155131DB3D56400368D35 /* ... */, 149 | 65ECB2BE1DB4134C00F96F46 /* Autogenerate */, 150 | ); 151 | sourceTree = ""; 152 | }; 153 | 65599E671DB3CFD300EC123C /* Supporting Files */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 65599E6A1DB3CFD300EC123C /* Money.h */, 157 | 65599E691DB3CFD300EC123C /* Info.plist */, 158 | 65599E681DB3CFD300EC123C /* Generate.swift */, 159 | 655155101DB3D45100368D35 /* Carthage.xcconfig */, 160 | 65599E6B1DB3CFD300EC123C /* Money.xcconfig */, 161 | 65599E6C1DB3CFD300EC123C /* Version.xcconfig */, 162 | 65AA39ED1DFEF418000942A7 /* Warnings.xcconfig */, 163 | ); 164 | path = "Supporting Files"; 165 | sourceTree = ""; 166 | }; 167 | 65599E731DB3D17E00EC123C /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 65599E721DB3D17E00EC123C /* Money.framework */, 171 | 65599E7C1DB3D17F00EC123C /* MoneyTests.xctest */, 172 | ); 173 | name = Products; 174 | sourceTree = ""; 175 | }; 176 | 65599E8A1DB3D2D500EC123C /* Sources */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 655155111DB3D50700368D35 /* ApplePay.swift */, 180 | 65599E8B1DB3D2D500EC123C /* Autogenerated.swift */, 181 | 65599E8C1DB3D2D500EC123C /* Bitcoin.swift */, 182 | 65599E8D1DB3D2D500EC123C /* Currency.swift */, 183 | 65599E8E1DB3D2D500EC123C /* Decimal.swift */, 184 | 65599E8F1DB3D2D500EC123C /* DecimalNumberType.swift */, 185 | 65599E901DB3D2D500EC123C /* Locale.swift */, 186 | 65599E911DB3D2D500EC123C /* Money.swift */, 187 | 65599E921DB3D2D500EC123C /* NSDecimalExtensions.swift */, 188 | 65599E931DB3D2D500EC123C /* NSDecimalNumberExtensions.swift */, 189 | ); 190 | path = Sources; 191 | sourceTree = ""; 192 | }; 193 | 65ECB2BE1DB4134C00F96F46 /* Autogenerate */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 65ECB2BF1DB4134C00F96F46 /* Autogenerate.h */, 197 | 65ECB2C01DB4134C00F96F46 /* Info.plist */, 198 | ); 199 | path = Autogenerate; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXGroup section */ 203 | 204 | /* Begin PBXHeadersBuildPhase section */ 205 | 65599E6F1DB3D17E00EC123C /* Headers */ = { 206 | isa = PBXHeadersBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 65ECB2B11DB3DC9900F96F46 /* Money.h in Headers */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXHeadersBuildPhase section */ 214 | 215 | /* Begin PBXNativeTarget section */ 216 | 65599E711DB3D17E00EC123C /* Money */ = { 217 | isa = PBXNativeTarget; 218 | buildConfigurationList = 65599E881DB3D17F00EC123C /* Build configuration list for PBXNativeTarget "Money" */; 219 | buildPhases = ( 220 | 65599E6D1DB3D17E00EC123C /* Sources */, 221 | 65599E6E1DB3D17E00EC123C /* Frameworks */, 222 | 65599E6F1DB3D17E00EC123C /* Headers */, 223 | 65599E701DB3D17E00EC123C /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | 652899921DB3D96B0099D541 /* PBXTargetDependency */, 229 | ); 230 | name = Money; 231 | productName = Money; 232 | productReference = 65599E721DB3D17E00EC123C /* Money.framework */; 233 | productType = "com.apple.product-type.framework"; 234 | }; 235 | 65599E7B1DB3D17F00EC123C /* MoneyTests */ = { 236 | isa = PBXNativeTarget; 237 | buildConfigurationList = 65599E891DB3D17F00EC123C /* Build configuration list for PBXNativeTarget "MoneyTests" */; 238 | buildPhases = ( 239 | 65599E781DB3D17F00EC123C /* Sources */, 240 | 65599E791DB3D17F00EC123C /* Frameworks */, 241 | 65599E7A1DB3D17F00EC123C /* Resources */, 242 | 65ECB2B21DB3DD9F00F96F46 /* Copy Carthage Frameworks */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | 65599E7F1DB3D17F00EC123C /* PBXTargetDependency */, 248 | ); 249 | name = MoneyTests; 250 | productName = MoneyTests; 251 | productReference = 65599E7C1DB3D17F00EC123C /* MoneyTests.xctest */; 252 | productType = "com.apple.product-type.bundle.unit-test"; 253 | }; 254 | /* End PBXNativeTarget section */ 255 | 256 | /* Begin PBXProject section */ 257 | 65599E611DB3CF2E00EC123C /* Project object */ = { 258 | isa = PBXProject; 259 | attributes = { 260 | LastSwiftUpdateCheck = 0730; 261 | LastUpgradeCheck = 0800; 262 | TargetAttributes = { 263 | 65599E711DB3D17E00EC123C = { 264 | CreatedOnToolsVersion = 7.3.1; 265 | LastSwiftMigration = 0800; 266 | }; 267 | 65599E7B1DB3D17F00EC123C = { 268 | CreatedOnToolsVersion = 7.3.1; 269 | }; 270 | 65687D5B1DB3D8B70016FB31 = { 271 | CreatedOnToolsVersion = 7.3.1; 272 | }; 273 | }; 274 | }; 275 | buildConfigurationList = 65599E641DB3CF2E00EC123C /* Build configuration list for PBXProject "Money" */; 276 | compatibilityVersion = "Xcode 3.2"; 277 | developmentRegion = English; 278 | hasScannedForEncodings = 0; 279 | knownRegions = ( 280 | en, 281 | ); 282 | mainGroup = 65599E601DB3CF2E00EC123C; 283 | productRefGroup = 65599E731DB3D17E00EC123C /* Products */; 284 | projectDirPath = ""; 285 | projectRoot = ""; 286 | targets = ( 287 | 65687D5B1DB3D8B70016FB31 /* Generate Swift */, 288 | 65599E711DB3D17E00EC123C /* Money */, 289 | 65599E7B1DB3D17F00EC123C /* MoneyTests */, 290 | ); 291 | }; 292 | /* End PBXProject section */ 293 | 294 | /* Begin PBXResourcesBuildPhase section */ 295 | 65599E701DB3D17E00EC123C /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | 65599E7A1DB3D17F00EC123C /* Resources */ = { 303 | isa = PBXResourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | }; 309 | /* End PBXResourcesBuildPhase section */ 310 | 311 | /* Begin PBXShellScriptBuildPhase section */ 312 | 65687D5F1DB3D8C30016FB31 /* Generate Swift */ = { 313 | isa = PBXShellScriptBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | inputPaths = ( 318 | "$(SRCROOT)/Supporting Files/Generate.swift", 319 | ); 320 | name = "Generate Swift"; 321 | outputPaths = ( 322 | "$(SRCROOT)/Sources/Autogenerated.swift", 323 | "$(SRCROOT)/Tests/AutogeneratedTests.swift", 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | shellPath = /bin/sh; 327 | shellScript = "\"$SCRIPT_INPUT_FILE_0\" \"$SCRIPT_OUTPUT_FILE_0\" \"$SCRIPT_OUTPUT_FILE_1\""; 328 | showEnvVarsInLog = 0; 329 | }; 330 | 65ECB2B21DB3DD9F00F96F46 /* Copy Carthage Frameworks */ = { 331 | isa = PBXShellScriptBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | ); 335 | inputPaths = ( 336 | ValueCoding, 337 | ); 338 | name = "Copy Carthage Frameworks"; 339 | outputPaths = ( 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | shellPath = /bin/sh; 343 | shellScript = "case \"$PLATFORM_NAME\" in\nmacosx) plat=Mac;;\niphone*) plat=iOS;;\nwatch*) plat=watchOS;;\nappletv*) plat=tvOS;;\n*) echo \"error: Unknown PLATFORM_NAME: $PLATFORM_NAME\"; exit 1;;\nesac\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nframework=$(basename \"${!VAR}\")\nexport SCRIPT_INPUT_FILE_$n=\"$SRCROOT\"/Carthage/Build/$plat/\"$framework\".framework\ndone\n\n/usr/local/bin/carthage copy-frameworks || exit\n\nfor (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do\nVAR=SCRIPT_INPUT_FILE_$n\nsource=${!VAR}.dSYM\ndest=${BUILT_PRODUCTS_DIR}/$(basename \"$source\")\nditto \"$source\" \"$dest\" || exit\ndone"; 344 | showEnvVarsInLog = 0; 345 | }; 346 | /* End PBXShellScriptBuildPhase section */ 347 | 348 | /* Begin PBXSourcesBuildPhase section */ 349 | 65599E6D1DB3D17E00EC123C /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | 65599E971DB3D2D500EC123C /* Currency.swift in Sources */, 354 | 65599E9D1DB3D2D500EC123C /* NSDecimalNumberExtensions.swift in Sources */, 355 | 65599E961DB3D2D500EC123C /* Bitcoin.swift in Sources */, 356 | 65599E9B1DB3D2D500EC123C /* Money.swift in Sources */, 357 | 65599E991DB3D2D500EC123C /* DecimalNumberType.swift in Sources */, 358 | 65599E9C1DB3D2D500EC123C /* NSDecimalExtensions.swift in Sources */, 359 | 65599E951DB3D2D500EC123C /* Autogenerated.swift in Sources */, 360 | 65599E981DB3D2D500EC123C /* Decimal.swift in Sources */, 361 | 655155121DB3D50700368D35 /* ApplePay.swift in Sources */, 362 | 65599E9A1DB3D2D500EC123C /* Locale.swift in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 65599E781DB3D17F00EC123C /* Sources */ = { 367 | isa = PBXSourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | 655155231DB3D59900368D35 /* DecimalTests.swift in Sources */, 371 | 655155271DB3D59900368D35 /* NSDecimalNumberTests.swift in Sources */, 372 | 655155221DB3D59900368D35 /* DecimalNumberTypeTests.swift in Sources */, 373 | 655155211DB3D59900368D35 /* BitcoinTests.swift in Sources */, 374 | 655155201DB3D59900368D35 /* AutogeneratedTests.swift in Sources */, 375 | 6551551F1DB3D59900368D35 /* ApplePayTests.swift in Sources */, 376 | 655155251DB3D59900368D35 /* LocaleTests.swift in Sources */, 377 | 655155281DB3D59900368D35 /* NSDecimalTests.swift in Sources */, 378 | 655155261DB3D59900368D35 /* MoneyTests.swift in Sources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | /* End PBXSourcesBuildPhase section */ 383 | 384 | /* Begin PBXTargetDependency section */ 385 | 652899921DB3D96B0099D541 /* PBXTargetDependency */ = { 386 | isa = PBXTargetDependency; 387 | target = 65687D5B1DB3D8B70016FB31 /* Generate Swift */; 388 | targetProxy = 652899911DB3D96B0099D541 /* PBXContainerItemProxy */; 389 | }; 390 | 65599E7F1DB3D17F00EC123C /* PBXTargetDependency */ = { 391 | isa = PBXTargetDependency; 392 | target = 65599E711DB3D17E00EC123C /* Money */; 393 | targetProxy = 65599E7E1DB3D17F00EC123C /* PBXContainerItemProxy */; 394 | }; 395 | /* End PBXTargetDependency section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | 65599E651DB3CF2E00EC123C /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | baseConfigurationReference = 65599E6B1DB3CFD300EC123C /* Money.xcconfig */; 401 | buildSettings = { 402 | ENABLE_TESTABILITY = YES; 403 | ONLY_ACTIVE_ARCH = YES; 404 | }; 405 | name = Debug; 406 | }; 407 | 65599E661DB3CF2E00EC123C /* Release */ = { 408 | isa = XCBuildConfiguration; 409 | baseConfigurationReference = 65599E6B1DB3CFD300EC123C /* Money.xcconfig */; 410 | buildSettings = { 411 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 412 | }; 413 | name = Release; 414 | }; 415 | 65599E841DB3D17F00EC123C /* Debug */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | CLANG_ANALYZER_NONNULL = YES; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_CONSTANT_CONVERSION = YES; 426 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INT_CONVERSION = YES; 430 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 431 | CLANG_WARN_UNREACHABLE_CODE = YES; 432 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 433 | CODE_SIGN_IDENTITY = ""; 434 | COMBINE_HIDPI_IMAGES = YES; 435 | COPY_PHASE_STRIP = NO; 436 | CURRENT_PROJECT_VERSION = 1; 437 | DEBUG_INFORMATION_FORMAT = dwarf; 438 | DEFINES_MODULE = YES; 439 | DYLIB_COMPATIBILITY_VERSION = 1; 440 | DYLIB_CURRENT_VERSION = 1; 441 | ENABLE_STRICT_OBJC_MSGSEND = YES; 442 | ENABLE_TESTABILITY = YES; 443 | FRAMEWORK_VERSION = A; 444 | GCC_C_LANGUAGE_STANDARD = gnu99; 445 | GCC_DYNAMIC_NO_PIC = NO; 446 | GCC_NO_COMMON_BLOCKS = YES; 447 | GCC_OPTIMIZATION_LEVEL = 0; 448 | GCC_PREPROCESSOR_DEFINITIONS = ( 449 | "DEBUG=1", 450 | "$(inherited)", 451 | ); 452 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 453 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 454 | GCC_WARN_UNDECLARED_SELECTOR = YES; 455 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 456 | GCC_WARN_UNUSED_FUNCTION = YES; 457 | GCC_WARN_UNUSED_VARIABLE = YES; 458 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 459 | MTL_ENABLE_DEBUG_INFO = YES; 460 | SKIP_INSTALL = YES; 461 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 462 | VERSIONING_SYSTEM = "apple-generic"; 463 | VERSION_INFO_PREFIX = ""; 464 | }; 465 | name = Debug; 466 | }; 467 | 65599E851DB3D17F00EC123C /* Release */ = { 468 | isa = XCBuildConfiguration; 469 | buildSettings = { 470 | ALWAYS_SEARCH_USER_PATHS = NO; 471 | CLANG_ANALYZER_NONNULL = YES; 472 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 473 | CLANG_CXX_LIBRARY = "libc++"; 474 | CLANG_ENABLE_MODULES = YES; 475 | CLANG_ENABLE_OBJC_ARC = YES; 476 | CLANG_WARN_BOOL_CONVERSION = YES; 477 | CLANG_WARN_CONSTANT_CONVERSION = YES; 478 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 479 | CLANG_WARN_EMPTY_BODY = YES; 480 | CLANG_WARN_ENUM_CONVERSION = YES; 481 | CLANG_WARN_INT_CONVERSION = YES; 482 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 483 | CLANG_WARN_UNREACHABLE_CODE = YES; 484 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 485 | CODE_SIGN_IDENTITY = ""; 486 | COMBINE_HIDPI_IMAGES = YES; 487 | COPY_PHASE_STRIP = NO; 488 | CURRENT_PROJECT_VERSION = 1; 489 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 490 | DEFINES_MODULE = YES; 491 | DYLIB_COMPATIBILITY_VERSION = 1; 492 | DYLIB_CURRENT_VERSION = 1; 493 | ENABLE_NS_ASSERTIONS = NO; 494 | ENABLE_STRICT_OBJC_MSGSEND = YES; 495 | FRAMEWORK_VERSION = A; 496 | GCC_C_LANGUAGE_STANDARD = gnu99; 497 | GCC_NO_COMMON_BLOCKS = YES; 498 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 499 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 500 | GCC_WARN_UNDECLARED_SELECTOR = YES; 501 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 502 | GCC_WARN_UNUSED_FUNCTION = YES; 503 | GCC_WARN_UNUSED_VARIABLE = YES; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 505 | MTL_ENABLE_DEBUG_INFO = NO; 506 | SKIP_INSTALL = YES; 507 | VERSIONING_SYSTEM = "apple-generic"; 508 | VERSION_INFO_PREFIX = ""; 509 | }; 510 | name = Release; 511 | }; 512 | 65599E861DB3D17F00EC123C /* Debug */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | ALWAYS_SEARCH_USER_PATHS = NO; 516 | CLANG_ANALYZER_NONNULL = YES; 517 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 518 | CLANG_CXX_LIBRARY = "libc++"; 519 | CLANG_ENABLE_MODULES = YES; 520 | CLANG_ENABLE_OBJC_ARC = YES; 521 | CLANG_WARN_BOOL_CONVERSION = YES; 522 | CLANG_WARN_CONSTANT_CONVERSION = YES; 523 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 524 | CLANG_WARN_EMPTY_BODY = YES; 525 | CLANG_WARN_ENUM_CONVERSION = YES; 526 | CLANG_WARN_INT_CONVERSION = YES; 527 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 528 | CLANG_WARN_UNREACHABLE_CODE = YES; 529 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 530 | CODE_SIGN_IDENTITY = "-"; 531 | COMBINE_HIDPI_IMAGES = YES; 532 | COPY_PHASE_STRIP = NO; 533 | DEBUG_INFORMATION_FORMAT = dwarf; 534 | ENABLE_STRICT_OBJC_MSGSEND = YES; 535 | ENABLE_TESTABILITY = YES; 536 | GCC_C_LANGUAGE_STANDARD = gnu99; 537 | GCC_DYNAMIC_NO_PIC = NO; 538 | GCC_NO_COMMON_BLOCKS = YES; 539 | GCC_OPTIMIZATION_LEVEL = 0; 540 | GCC_PREPROCESSOR_DEFINITIONS = ( 541 | "DEBUG=1", 542 | "$(inherited)", 543 | ); 544 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 545 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 546 | GCC_WARN_UNDECLARED_SELECTOR = YES; 547 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 548 | GCC_WARN_UNUSED_FUNCTION = YES; 549 | GCC_WARN_UNUSED_VARIABLE = YES; 550 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 551 | MACOSX_DEPLOYMENT_TARGET = 10.12; 552 | MTL_ENABLE_DEBUG_INFO = YES; 553 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 554 | TVOS_DEPLOYMENT_TARGET = 10.0; 555 | WATCHOS_DEPLOYMENT_TARGET = 3.0; 556 | }; 557 | name = Debug; 558 | }; 559 | 65599E871DB3D17F00EC123C /* Release */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ALWAYS_SEARCH_USER_PATHS = NO; 563 | CLANG_ANALYZER_NONNULL = YES; 564 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 565 | CLANG_CXX_LIBRARY = "libc++"; 566 | CLANG_ENABLE_MODULES = YES; 567 | CLANG_ENABLE_OBJC_ARC = YES; 568 | CLANG_WARN_BOOL_CONVERSION = YES; 569 | CLANG_WARN_CONSTANT_CONVERSION = YES; 570 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 571 | CLANG_WARN_EMPTY_BODY = YES; 572 | CLANG_WARN_ENUM_CONVERSION = YES; 573 | CLANG_WARN_INT_CONVERSION = YES; 574 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 575 | CLANG_WARN_UNREACHABLE_CODE = YES; 576 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 577 | CODE_SIGN_IDENTITY = "-"; 578 | COMBINE_HIDPI_IMAGES = YES; 579 | COPY_PHASE_STRIP = NO; 580 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 581 | ENABLE_NS_ASSERTIONS = NO; 582 | ENABLE_STRICT_OBJC_MSGSEND = YES; 583 | GCC_C_LANGUAGE_STANDARD = gnu99; 584 | GCC_NO_COMMON_BLOCKS = YES; 585 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 586 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 587 | GCC_WARN_UNDECLARED_SELECTOR = YES; 588 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 589 | GCC_WARN_UNUSED_FUNCTION = YES; 590 | GCC_WARN_UNUSED_VARIABLE = YES; 591 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 592 | MACOSX_DEPLOYMENT_TARGET = 10.12; 593 | MTL_ENABLE_DEBUG_INFO = NO; 594 | TVOS_DEPLOYMENT_TARGET = 10.0; 595 | WATCHOS_DEPLOYMENT_TARGET = 3.0; 596 | }; 597 | name = Release; 598 | }; 599 | 65687D5C1DB3D8B70016FB31 /* Debug */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | PRODUCT_NAME = "$(TARGET_NAME)"; 603 | }; 604 | name = Debug; 605 | }; 606 | 65687D5D1DB3D8B70016FB31 /* Release */ = { 607 | isa = XCBuildConfiguration; 608 | buildSettings = { 609 | PRODUCT_NAME = "$(TARGET_NAME)"; 610 | }; 611 | name = Release; 612 | }; 613 | /* End XCBuildConfiguration section */ 614 | 615 | /* Begin XCConfigurationList section */ 616 | 65599E641DB3CF2E00EC123C /* Build configuration list for PBXProject "Money" */ = { 617 | isa = XCConfigurationList; 618 | buildConfigurations = ( 619 | 65599E651DB3CF2E00EC123C /* Debug */, 620 | 65599E661DB3CF2E00EC123C /* Release */, 621 | ); 622 | defaultConfigurationIsVisible = 0; 623 | defaultConfigurationName = Release; 624 | }; 625 | 65599E881DB3D17F00EC123C /* Build configuration list for PBXNativeTarget "Money" */ = { 626 | isa = XCConfigurationList; 627 | buildConfigurations = ( 628 | 65599E841DB3D17F00EC123C /* Debug */, 629 | 65599E851DB3D17F00EC123C /* Release */, 630 | ); 631 | defaultConfigurationIsVisible = 0; 632 | defaultConfigurationName = Release; 633 | }; 634 | 65599E891DB3D17F00EC123C /* Build configuration list for PBXNativeTarget "MoneyTests" */ = { 635 | isa = XCConfigurationList; 636 | buildConfigurations = ( 637 | 65599E861DB3D17F00EC123C /* Debug */, 638 | 65599E871DB3D17F00EC123C /* Release */, 639 | ); 640 | defaultConfigurationIsVisible = 0; 641 | defaultConfigurationName = Release; 642 | }; 643 | 65687D5E1DB3D8B70016FB31 /* Build configuration list for PBXAggregateTarget "Generate Swift" */ = { 644 | isa = XCConfigurationList; 645 | buildConfigurations = ( 646 | 65687D5C1DB3D8B70016FB31 /* Debug */, 647 | 65687D5D1DB3D8B70016FB31 /* Release */, 648 | ); 649 | defaultConfigurationIsVisible = 0; 650 | defaultConfigurationName = Release; 651 | }; 652 | /* End XCConfigurationList section */ 653 | }; 654 | rootObject = 65599E611DB3CF2E00EC123C /* Project object */; 655 | } 656 | -------------------------------------------------------------------------------- /Money.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Money.xcodeproj/xcshareddata/xcschemes/Money.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.githubusercontent.com/danthorpe/Money/development/header.png) 2 | 3 | [![Build status](https://badge.buildkite.com/265eb9670a2ef6b73eebf37769a8455c402509f71f09c4f51e.svg?branch=development)](https://buildkite.com/blindingskies/money?branch=development) 4 | [![Coverage Status](https://coveralls.io/repos/github/danthorpe/Money/badge.svg?branch=development)](https://coveralls.io/github/danthorpe/Money?branch=development) 5 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Money.svg)](https://img.shields.io/cocoapods/v/Money.svg) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![Platform](https://img.shields.io/cocoapods/p/Money.svg?style=flat)](http://cocoadocs.org/docsets/Money) 8 | 9 | # Money 10 | 11 | Money is a Swift framework for iOS, watchOS, tvOS and OS X. It provides types and functionality to represent, calculate and convert money in the 298 [ISO currencies](https://en.wikipedia.org/wiki/ISO_4217). 12 | 13 | --- 14 | 15 | ## Usage 16 | 17 | The Money framework defines the type `Money`, which represents money in the device’s current locale. The following code: 18 | 19 | ```swift 20 | import Money 21 | 22 | let money: Money = 100 23 | print("I'll give \(money) to charity.”) 24 | ``` 25 | 26 | will print out 27 | 28 | > I'll give $100.00 to charity 29 | 30 | when the region is set to United States 31 | 32 | > I'll give £100.00 to charity 33 | 34 | when the region is set to United Kingdom 35 | 36 | > I'll give CN¥100.00 to charity 37 | 38 | when the region is set to China 39 | 40 | You get the idea. See [Localized Formatting](#localized-formatting) for more info. 41 | 42 | `Money` is `IntegerLiteralConvertible` and `FloatLiteralConvertible`. Which means values can be initialized using literal `Int`s and `Double`s as shown in these code snippets. 43 | 44 | ## Specific Currency 45 | 46 | Under the hood, `Money` is a `typealias` for `_Money` where `Currency.Local` is a specific `CurrencyType` which represents the currency for the current locale. This means that it is strongly typed to the local currency. 47 | 48 | In a similar way, there are 298 foreign currency types supported. 49 | 50 | ```swift 51 | let pounds: GBP = 99.99 52 | let euros: EUR = 149.50 53 | 54 | print(“You have \(pounds / 2) and \(euros + 30)”) 55 | ``` 56 | 57 | > You have £ 50.00 and € 179.50 58 | 59 | Because the currencies are typed, it means that they cannot be combined together. 60 | 61 | ```swift 62 | let money = pounds + euros 63 | ``` 64 | > Binary operator '+' cannot be applied to operands of type 'GBP' (aka '_Money<Currency.GBP>') and 'EUR' (aka '_Money<Currency.EUR>') 65 | 66 | Of course, `Money` supports the usual suspects of decimal arithmetic operators, so you can add, subtract, multiply, divide values of the same type, and values with `Int` and `Double` with the expected limitations. 67 | 68 | ## Convenience initializers 69 | 70 | `Money` (and its friends) can be initialized with `Int`s (and friends) and`Double`s. 71 | 72 | ```swift 73 | let anIntegerFromSomewhereElse: Int = getAnInteger() 74 | let money = Money(anIntegerFromSomewhereElse) 75 | 76 | let aDoubleFromSomewhere: Double = getAnotherDouble() 77 | let pounds = GBP(aDoubleFromSomewhere) 78 | ``` 79 | 80 | ### Minor Units 81 | 82 | `Money` can be initialized using the smallest units of currency: 83 | 84 | ```swift 85 | let dollars = USD(minorUnits: 3250) 86 | let yuen = JPY(minorUnits: 3000) 87 | 88 | print(“You have \(dollars) and \(yuen)”) 89 | ``` 90 | 91 | > You have $32.50 and ¥3,000 92 | 93 | ## Localized Formatting 94 | 95 | When displaying money values, it is important that they be correctly localized for the user. In general, it’s best to use the `Money` type to always work in currency of the user’s current locale. 96 | 97 | When printing a `MoneyType` value, the `.description` uses the current locale with `.CurrencyStyle` number style, in conjunction with [`NSNumberFormatter`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNumberFormatter_Class/index.html). The code snippets throughout this README uses `.description` whenever the value of money is printed. 98 | 99 | However, to specify a different style for the number formatter, use the `formattedWithStyle` method, like this: 100 | 101 | ```swift 102 | let money: Money = 99.99 103 | print("She has \(money.formattedWithStyle(.CurrencyPluralStyle))") 104 | ``` 105 | 106 | For an American in Russia, this would print out: 107 | >She has 99,99 Russian roubles 108 | 109 | ### Working with Locales 110 | 111 | A *locale* is the codification of associated regional and linguistic attributes. A locale varies by language and region. Each locale has an [identifier](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes), which is the concatenation of language, country and modifier codes. 112 | 113 | The language code is two or three lowercase letters. English is `en`, French is `fr`. There is a [long list](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). Some languages are spoken in more than one country, in which case a country code (two uppercase letters) is appended (with an underscore). For example, English in the United States is `en_US`, which is the default locale in the iOS Simulator. English in the United Kingdom is `en_GB`. 114 | 115 | Lastly, a locale identifier can be modified, say for example to set the currency code to “USD”, for Portuguese speaking user in Brazil, the locale identifier would be `pt_BR@currency=USD`. 116 | 117 | In total, `NSLocale` has support for ~ 730 distinct locales. Typically when creating a specific `NSLocale` it is done with the locale identifier. The `NSLocale` is like a dictionary with an `objectForKey` method which returns `AnyObject!`. 118 | 119 | ### Formatting for specific Locale 120 | 121 | I think `NSLocale` is an amazing class, but it’s very easy to make mistakes, and not that easy to construct. Therefore, to support arbitrary locales, but remove the need for framework consumers to construct locale identifiers, a new `Locale` type is provided. This is an enum which means that it is type safe, and indexable for code completion in Xcode. Its cases are all the languages which `NSLocale` supports. For those languages which are spoken in more than one country, there is an associated value of country names of only those counties. 122 | 123 | To format money for a specific locale we can use the `Locale` enum. The following code uses `Locale.Chinese(.China)` to represent the `"zh_CN"` locale. 124 | 125 | ```swift 126 | let money: Money = 99.99 127 | print("She has \(money.formattedWithStyle(.CurrencyPluralStyle, forLocale: .Chinese(.China)))") 128 | ``` 129 | 130 | Now, for our American in Russia, (or any user with a region set to Russia) we get: 131 | >She has 99.99俄罗斯卢布 132 | 133 | In this case, because our type is `Money`, and the user’s region is set to Russia, we’re working with `RUB` currency. But equally, if we need money in a specific currency, we can. Here’s Australian dollars, for a SwissGerman speaking user, in France. 134 | 135 | ```swift 136 | let dollars: AUD = 39.99 137 | print("You’ll need \((dollars / 2.5).formattedWithStyle(.CurrencyPluralStyle, forLocale: .SwissGerman(.France)))") 138 | ``` 139 | Regardless of the user’s current locale, this will print out: 140 | >You’ll need 16.00 Auschtralischi Dollar 141 | 142 | ##  Pay 143 | 144 | On iOS (not watchOS, tvOS or OS X), there is support in Money for using `Money` with  Pay. 145 | 146 | Create a `PaymentSummaryItem` in lieu of `PKPaymentSummaryItem` with a suitable `MoneyType`: 147 | 148 | ```swift 149 | import PassKit 150 | 151 | typealias DollarItem = PaymentSummaryItem 152 | 153 | let items = [ 154 | DollarItem(label: "Something fancy.", cost: 9.99), 155 | DollarItem(label: "Something less fancy.", cost: 5.99) 156 | ] 157 | 158 | let request = PKPaymentRequest(items: items, sellerName: "Acme, Inc.") 159 | ``` 160 | 161 | The convenience initializer receives an array of `PaymentSummaryItem` values and a seller name. It sets the currency code and payment summary items. Following the  Pay guidelines, will append a total summary item using the provided seller name. 162 | 163 | `PaymentSummaryItem` conforms to `Hashable` and [`ValueCoding`](https://github.com/danthorpe/ValueCoding). 164 | 165 | ## Bitcoin 166 | 167 | Money has support for Bitcoin types, the popular `BTC` and the unofficial ISO 4217 currency code `XBT`. 168 | 169 | In [November 2015](http://www.coindesk.com/bitcoin-unicode-symbol-approval/), the Unicode consortium accepted U+20BF as the Bitcoin symbol. However, right now that means it is not available in Foundation. Therefore, currently the Bitcoin currency type(s) use Ƀ, which is also a popular symbol and available already within Unicode. 170 | 171 | To work with Bitcoin, use the following: 172 | 173 | ```swift 174 | let bitcoin: BTC = 0.1234_5678 175 | print(“You have \(bitcoin)”) 176 | ``` 177 | > You have Ƀ0.12345678 178 | 179 | ## Foreign Exchange (FX) 180 | 181 | The FX support which was previously part of this framework has been moved into its own, called [FX](https://github.com/danthorpe/FX). 182 | 183 | # Creating custom currencies 184 | 185 | If your app has its own currency e.g. ⭐️s or 💎s or even 🐝s, you might want to consider making a type for it. 186 | 187 | Lets imagine we’re making *Hive.app* - where you compete with your friends to see who can get the biggest hive (measured in number of 🐝s). 188 | 189 | To create a custom currency, just conform to `CurrencyType`. 190 | 191 | ```swift 192 | protocol HiveCurrencyType: CustomCurrencyType { } 193 | 194 | extension Currency { 195 | final class Bee: HiveCurrencyType { 196 | 197 | static let code: String = "BEES" 198 | static let symbol: String = "🐝" 199 | static let scale: Int = 0 200 | } 201 | } 202 | 203 | typealias Bees = _Money 204 | ``` 205 | 206 | Just make sure that your currency code doesn’t clash with a real one - make it more than three characters to be sure. 207 | 208 | Now it’s possible to work with your own app’s currency as a proper money type. 209 | 210 | ```swift 211 | let bees: Bees = 10_000 212 | print(“I have \(bees)”) 213 | ``` 214 | > I have 🐝10,000 215 | 216 | And of course if you have an IAP for purchasing in-app currency, then I’m sure a custom FX provider would be handy. 217 | 218 | Take a look at the example project, [Custom Money](https://github.com/danthorpe/Examples/tree/development/Money/Custom%20Money), for an example of a custom local FX provider to exchange your 🐝s. 219 | 220 | ## Installation 221 | Money builds as a cross platform (iOS, OS X, watchOS) extension compatible framework. It is compatible with [Carthage](https://github.com/carthage/carthage). It is also available via CocoaPods. 222 | 223 | ```ruby 224 | pod ‘Money’ 225 | ``` 226 | 227 | At of writing there are some issues with the CocoaDocs generator for pure Swift 2 projects. This means that the project doesn’t have a page/docs in CocoaPods sites, however they are available through Xcode. 228 | 229 | --- 230 | 231 | ## Architectural style 232 | Swift is designed to have a focus on safety, enabled primarily through strong typing. This framework fully embraces this ethos and uses generics heavily to achieve this goal. 233 | 234 | At the highest level *currency* is modeled as a protocol, `CurrencyType`. The protocol defines a few static properties like its symbol, and currency code. Therefore *money* is represented as a decimal number with a generic currency. Additionally, we make `CurrencyType` refine the protocol which defines how the decimal number behaves. 235 | 236 | Finally, we auto-generate the code which defines all the currencies and money typealiases. 237 | 238 | ## Implementation Details 239 | 240 | Cocoa has two type which can perform decimal arithmetic, these are `NSDecimalNumber` and `NSDecimal`. `NSDecimal` is faster, but is trickier to work with, and doesn’t have support for limiting the scale of the numbers (which is pretty important when working with currencies). 241 | 242 | `DecimalNumberType` is a protocol which refines `SignedNumberType` and defines its own functions, `add`, `subtract` etc to support the arithmetic. It is generic over two types, the underlying storage, and the behaviors. 243 | 244 | `DecimalNumberType.DecimalStorageType` exists so that conforming types can utilize either `NSDecimalNumber` or `NSDecimal` as their underling storage type. 245 | 246 | `DecimalNumberBehavior` is a protocol which exposes a [`NSDecimalNumberBehaviors`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSDecimalNumberBehaviors_Protocol/) which should be used in calculations. This includes rounding style, scale, and when to throw exceptions. 247 | 248 | ### `_Decimal` 249 | 250 | Which leads us to `_Decimal` which is a value type implementing `DecimalNumberType` with an `NSDecimalNumber` storage type. 251 | 252 | There are two public typealiases for convenience. 253 | 254 | ```swift 255 | /// `Decimal` with plain decimal number behavior 256 | public typealias Decimal = _Decimal 257 | 258 | /// `BankersDecimal` with banking decimal number behavior 259 | public typealias BankersDecimal = _Decimal 260 | ``` 261 | 262 | This means, that `Decimal` is more than likely the type to use for most things. 263 | 264 | ### `_Money` 265 | The `_Money` type composes a `_Decimal`. Its behavior is provided via its generic `CurrencyType` which refines `DecimalNumberBehavior`. `_Money` also conforms to `DecimalNumberType` which means that it can also be used with the operators. 266 | 267 | ### Why not use `NSDecimal`? 268 | `NSDecimal` would be a better storage type for `_Decimal`, however it doesn’t have the full `NSDecimalNumberBehaviors` support that `NSDecimalNumber` enjoys. In particular, specifying the scale is problematic. If anyone has any smart ideas, please get in touch. I’ve added an equivalent extension on `NSDecimal` as for `NSDecimalNumber`. 269 | 270 | ### `ValueCoding` 271 | Both `_Decimal`, `_Money` and `FXTransaction` all conform to [`ValueCoding`](https://github.com/danthorpe/ValueCoding) which means they can be encoded and stored inside archives. 272 | 273 | 274 | ## Author 275 | Daniel Thorpe [@danthorpe](https://twitter.com/danthorpe). 276 | 277 | Feel free to get in contact if you have questions, queries, suggestions, or need help. Especially get in contact via an Issue here or on Twitter if you want to add support for another FX service provider. 278 | 279 | I wrote an introductory blog post about money [here](http://danthorpe.me/posts/money.html). 280 | 281 | ## License 282 | 283 | Money is available under the MIT license. See the LICENSE file for more info. 284 | 285 | ## Disclaimer 286 | 287 | Usage of this framework prevents the author, Daniel Thorpe, from being held liable for any losses incurred by the user through their use of the framework. 288 | -------------------------------------------------------------------------------- /Sources/ApplePay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | #if os(iOS) 28 | 29 | import Foundation 30 | import PassKit 31 | import ValueCoding 32 | 33 | // MARK: - Pay equivalent types 34 | 35 | /** 36 | 37 | # PaymentSummaryItemType 38 | 39 | An equivalent `PKPaymentSummaryItemType` enum. While defined 40 | for iOS 8, usage will only have an impact on iOS 9. 41 | 42 | - see: PKPaymentSummaryItemType 43 | */ 44 | public enum PaymentSummaryItemType: Int { 45 | case final = 1, pending 46 | } 47 | 48 | /** 49 | 50 | # PaymentSummaryItem 51 | 52 | A value type to represent a payment line item. It is generic over the 53 | `MoneyType` of the item cost. Other properties are a label and type. 54 | 55 | The money type must use `NSDecimalNumber` storage type, and correctly 56 | conform to `ValueCoding`. 57 | */ 58 | public struct PaymentSummaryItem: Hashable, ValueCoding where Cost.DecimalStorageType == NSDecimalNumber, Cost.Coder: NSCoding, Cost.Coder.Value == Cost { 59 | 60 | /// The ValueCoding Coder type 61 | public typealias Coder = PaymentSummaryItemCoder 62 | 63 | /** 64 | A label for the item. 65 | - returns: a `String` value 66 | */ 67 | public let label: String 68 | 69 | /** 70 | The cost of the item. 71 | - returns: a `Cost` value 72 | */ 73 | public let cost: Cost 74 | 75 | /** 76 | The cost type of the item. See docs for 77 | `PKPaymentSummaryItemType`. 78 | - returns: a `PaymentSummaryItemType` value 79 | */ 80 | public let type: PaymentSummaryItemType 81 | 82 | internal var amount: Cost.DecimalStorageType { 83 | return cost.amount 84 | } 85 | 86 | public var hashValue: Int { 87 | return cost.hashValue ^ (label.hashValue ^ type.hashValue) 88 | } 89 | 90 | /** 91 | Create a new `PaymentSummaryItem` with a cost, label and type. 92 | 93 | - discussion: As per the documentation of `PKPaymentSummaryItem` use 94 | a `.Pending` item type, for when the final value is not known 95 | yet, e.g. a taxi fare. In which case, the cost should be zero. On iOS 96 | 9 we will automaticaly set the cost to zero for pending type. 97 | 98 | - parameter label: the value for the `label` property. 99 | - parameter cost: the value for the `cost` property. 100 | - parameter type: the value for the `type` property. 101 | - returns: a summary item with a given label, cost and type. 102 | */ 103 | public init(label: String, cost: Cost, type: PaymentSummaryItemType = .final) { 104 | self.label = label 105 | self.type = type 106 | switch type { 107 | case .final: 108 | self.cost = cost 109 | case .pending: 110 | self.cost = 0 111 | } 112 | } 113 | } 114 | 115 | extension PaymentSummaryItem { 116 | 117 | /** 118 | Immutable setter for `label` property 119 | - parameter newLabel: the value for the `label` property in an item copy 120 | - returns: a summary item with a new label value, and previously set cost and type. 121 | */ 122 | public func set(label newLabel: String) -> PaymentSummaryItem { 123 | return PaymentSummaryItem(label: newLabel, cost: cost, type: type) 124 | } 125 | 126 | /** 127 | Immutable setter for `cost` property 128 | - parameter newCost: the value for the `cost` property in an item copy 129 | - returns: a summary item with a new cost value, and previously set label and type. 130 | */ 131 | public func set(cost newCost: Cost) -> PaymentSummaryItem { 132 | return PaymentSummaryItem(label: label, cost: newCost, type: type) 133 | } 134 | 135 | /** 136 | Immutable setter for `type` property 137 | - parameter newType: the value for the `type` property in an item copy 138 | - returns: a summary item with a new type value, and previously set label and cost. 139 | */ 140 | public func set(type newType: PaymentSummaryItemType) -> PaymentSummaryItem { 141 | return PaymentSummaryItem(label: label, cost: cost, type: newType) 142 | } 143 | } 144 | 145 | /** 146 | Coding adaptor for `PaymentSummaryItem`. 147 | */ 148 | public final class PaymentSummaryItemCoder: NSObject, NSCoding, CodingProtocol where Cost.DecimalStorageType == NSDecimalNumber, Cost.Coder: NSCoding, Cost.Coder.Value == Cost { 149 | 150 | public let value: PaymentSummaryItem 151 | 152 | public required init(_ v: PaymentSummaryItem) { 153 | value = v 154 | } 155 | 156 | public init?(coder aDecoder: NSCoder) { 157 | let cost = Cost.decode(aDecoder.decodeObject(forKey: "cost") as AnyObject?) 158 | let label = aDecoder.decodeObject(forKey: "label") as? String 159 | let type = PaymentSummaryItemType(rawValue: aDecoder.decodeInteger(forKey: "type")) 160 | value = PaymentSummaryItem(label: label!, cost: cost!, type: type!) 161 | } 162 | 163 | public func encode(with aCoder: NSCoder) { 164 | aCoder.encode(value.label, forKey: "label") 165 | aCoder.encode(value.cost.encoded, forKey: "cost") 166 | aCoder.encode(value.type.rawValue, forKey: "type") 167 | } 168 | } 169 | 170 | // MARK: - Pay type extensions 171 | 172 | @available(iOS 9.0, iOSApplicationExtension 9.0, *) 173 | internal extension PKPaymentSummaryItemType { 174 | 175 | init(paymentSummaryItemType: PaymentSummaryItemType) { 176 | switch paymentSummaryItemType { 177 | case .final: 178 | self = .final 179 | case .pending: 180 | self = .pending 181 | } 182 | } 183 | } 184 | 185 | internal extension PKPaymentSummaryItem { 186 | 187 | convenience init(paymentSummaryItem: PaymentSummaryItem) where Cost.DecimalStorageType == NSDecimalNumber { 188 | self.init() 189 | amount = paymentSummaryItem.amount 190 | label = paymentSummaryItem.label 191 | if #available(iOS 9.0, iOSApplicationExtension 9.0, *) { 192 | type = PKPaymentSummaryItemType(paymentSummaryItemType: paymentSummaryItem.type) 193 | } 194 | } 195 | } 196 | 197 | public extension PKPaymentRequest { 198 | 199 | /** 200 | Create a payment request with a sequence of `PaymentSummaryItem`s. The 201 | currency code will automatically be set. 202 | 203 | As per the guidlines the total cost is calculated and appended to the 204 | end of the list, using your company or seller name as the label. 205 | 206 | - see: [guideline](https://developer.apple.com/library/ios/ApplePay_Guide/CreateRequest.html) 207 | 208 | - parameter items: an array of `PaymentSummaryItem` values. 209 | - parameter sellerName: a `String` which is used in the total cost summary item. 210 | - returns: a `PKPaymentRequest` which has its payment summary items and currency code set. 211 | */ 212 | convenience init(items: [PaymentSummaryItem], sellerName: String) where Cost.DecimalStorageType == NSDecimalNumber, Cost.Coder: NSCoding, Cost.Coder.Value == Cost { 213 | self.init() 214 | currencyCode = Cost.Currency.code 215 | var items = items 216 | let total = items.map { $0.cost }.reduce(0, +) 217 | items.append(PaymentSummaryItem(label: sellerName, cost: total)) 218 | paymentSummaryItems = items.map { PKPaymentSummaryItem(paymentSummaryItem: $0) } 219 | } 220 | } 221 | 222 | // MARK: - Equality 223 | 224 | public func ==(lhs: PaymentSummaryItem, rhs: PaymentSummaryItem) -> Bool where Cost.DecimalStorageType == NSDecimalNumber { 225 | return lhs.cost == rhs.cost && lhs.label == rhs.label && lhs.type == rhs.type 226 | } 227 | 228 | #endif 229 | -------------------------------------------------------------------------------- /Sources/Bitcoin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | // MARK: - Bitcoin Currency 31 | 32 | /** 33 | # Bitcoin Currency Type 34 | 35 | BitcoinCurrencyType is a refinement of CryptoCurrencyType, 36 | which allows type restriction when working with Bitcoin. 37 | */ 38 | public protocol BitcoinCurrencyType: CryptoCurrencyType { } 39 | 40 | public extension BitcoinCurrencyType { 41 | 42 | /// The smallest unit of Bitcoin is the Satoshi 43 | /// - see: https://en.bitcoin.it/wiki/Satoshi_(unit) 44 | static var scale: Int { 45 | return 8 46 | } 47 | 48 | /// - returns: the currency symbol 49 | static var symbol: String? { 50 | return "Ƀ" 51 | } 52 | } 53 | 54 | public extension Currency { 55 | 56 | /** 57 | # Currency.XBT 58 | This is the ISO 4217 currency code, however at the moment 59 | it is unofficial. 60 | 61 | unicode \u{20bf} was accepted as the Bitcoin currency 62 | symbol in November. However, it's not yet available 63 | on Apple platforms. Ƀ is a popular alternative 64 | which is available. 65 | 66 | */ 67 | struct XBT: BitcoinCurrencyType { 68 | 69 | /// - returns: the proposed ISO 4217 currency code 70 | public static let code = "XBT" 71 | 72 | /// - returns: a configured NSNumberFormatter 73 | // public static let formatter: NSNumberFormatter = { 74 | // let fmtr = NSNumberFormatter() 75 | // fmtr.numberStyle = .CurrencyStyle 76 | // fmtr.maximumFractionDigits = scale 77 | // fmtr.currencySymbol = "Ƀ" 78 | // return fmtr 79 | // }() 80 | } 81 | 82 | /** 83 | # Currency.BTC 84 | This is the common code used for Bitcoin, although it can never become 85 | the ISO standard as BT is the country code for Bhutan. 86 | */ 87 | struct BTC: BitcoinCurrencyType { 88 | public static let code = "BTC" 89 | // public static let scale = Currency.XBT.scale 90 | // public static let formatter = Currency.XBT.formatter 91 | } 92 | } 93 | 94 | /// The proposed ISO 4217 Bitcoin MoneyType 95 | public typealias XBT = _Money 96 | 97 | /// The most commonly used Bitcoin MoneyType 98 | public typealias BTC = _Money 99 | 100 | 101 | -------------------------------------------------------------------------------- /Sources/Currency.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /** 30 | # CurrencyType 31 | This protocol defines the minimum interface needed for a 32 | CurrencyType. 33 | 34 | The interface used to represent a single currency. Note 35 | that it is always used as a generic constraint on other 36 | types. 37 | 38 | Typically framework consumers will not need to conform to 39 | this protocol, unless creating their own currencies. See 40 | the example project "Custom Money" for an example of this. 41 | */ 42 | public protocol CurrencyType: DecimalNumberBehaviorType { 43 | 44 | /// The currency code 45 | static var code: String { get } 46 | 47 | /// The currency scale 48 | static var scale: Int { get } 49 | 50 | /// The currency symbol 51 | static var symbol: String? { get } 52 | 53 | /// Default formatting style 54 | static var defaultFormattingStyle: NumberFormatter.Style { get } 55 | 56 | static func formatted(withStyle: NumberFormatter.Style, forLocaleId localeId: String) -> (NSDecimalNumber) -> String 57 | 58 | static func formatted(withStyle: NumberFormatter.Style, forLocale locale: MNYLocale) -> (NSDecimalNumber) -> String 59 | } 60 | 61 | public extension CurrencyType { 62 | 63 | static var defaultFormattingStyle: NumberFormatter.Style { 64 | return .currency 65 | } 66 | 67 | /** 68 | Default implementation of the `NSDecimalNumberBehaviors` for 69 | the currency. This uses `NSRoundingMode.RoundBankers` and the 70 | scale of the currency as given by the localized formatter. 71 | 72 | - returns: a `NSDecimalNumberBehaviors` 73 | */ 74 | static var decimalNumberBehaviors: NSDecimalNumberBehaviors { 75 | return NSDecimalNumberHandler( 76 | roundingMode: .bankers, 77 | scale: Int16(scale), 78 | raiseOnExactness: true, 79 | raiseOnOverflow: true, 80 | raiseOnUnderflow: true, 81 | raiseOnDivideByZero: true 82 | ) 83 | } 84 | 85 | /** 86 | Use the provided locale identifier to format a supplied NSDecimalNumber. 87 | 88 | - returns: a NSDecimalNumber -> String closure. 89 | */ 90 | static func formatted(withStyle style: NumberFormatter.Style, forLocaleId localeId: String) -> (NSDecimalNumber) -> String { 91 | let locale = NSLocale(localeIdentifier: NSLocale.canonicalLocaleIdentifier(from: localeId)) 92 | return formatted(withStyle: style, forLocale: locale) 93 | } 94 | 95 | static func formatted(withStyle style: NumberFormatter.Style, forLocale tmp: NSLocale) -> (NSDecimalNumber) -> String { 96 | 97 | let id = "\(tmp.localeIdentifier)@currency=\(code)" 98 | let canonical = NSLocale.canonicalLocaleIdentifier(from: id) 99 | let nslocale = NSLocale(localeIdentifier: canonical) 100 | let locale = Locale(identifier: canonical) 101 | 102 | let formatter = NumberFormatter() 103 | formatter.currencyCode = code 104 | formatter.locale = locale 105 | formatter.numberStyle = style 106 | formatter.maximumFractionDigits = scale 107 | formatter.currencySymbol = symbol ?? nslocale.mny_currencySymbol 108 | 109 | return { formatter.string(from: $0)! } 110 | } 111 | 112 | /** 113 | Use the provided Local to format a supplied NSDecimalNumber. 114 | 115 | - returns: a NSDecimalNumber -> String closure. 116 | */ 117 | static func formatted(withStyle style: NumberFormatter.Style, forLocale locale: MNYLocale) -> (NSDecimalNumber) -> String { 118 | return formatted(withStyle: style, forLocaleId: locale.localeIdentifier) 119 | } 120 | } 121 | 122 | /** 123 | Custom currency types should refine CustomCurrencyType. 124 | */ 125 | public protocol CustomCurrencyType: CurrencyType { } 126 | 127 | /** 128 | Crypto currency types (Bitcoin etc) should refine CryptoCurrencyType. 129 | */ 130 | public protocol CryptoCurrencyType: CustomCurrencyType { } 131 | 132 | /** 133 | `ISOCurrencyType` is a refinement of `CurrencyType` so that 134 | the ISO currencies can be autogenerated. 135 | */ 136 | public protocol ISOCurrencyType: CurrencyType { 137 | 138 | /** 139 | A shared instance of the currency. Note that static 140 | variables are lazily created. 141 | */ 142 | static var sharedInstance: Self { get } 143 | 144 | /// - returns: the currency code 145 | var _code: String { get } 146 | 147 | /// - returns: the currency scale 148 | var _scale: Int { get } 149 | 150 | /// - returns: the currency symbol 151 | var _symbol: String? { get } 152 | 153 | } 154 | 155 | public extension ISOCurrencyType { 156 | 157 | /// The currency code 158 | static var code: String { 159 | return sharedInstance._code 160 | } 161 | 162 | /// The currency scale 163 | static var scale: Int { 164 | return sharedInstance._scale 165 | } 166 | 167 | /// The currency symbol 168 | static var symbol: String? { 169 | return sharedInstance._symbol 170 | } 171 | 172 | /** 173 | Use the provided locale identifier to format a supplied NSDecimalNumber. 174 | 175 | - returns: a NSDecimalNumber -> String closure. 176 | */ 177 | static func formatted(withStyle style: NumberFormatter.Style, forLocaleId localeId: String) -> (NSDecimalNumber) -> String { 178 | let id = "\(NSLocale.current.identifier)@currency=\(code)" 179 | let locale = NSLocale(localeIdentifier: NSLocale.canonicalLocaleIdentifier(from: id)) 180 | return formatted(withStyle: style, forLocale: locale) 181 | } 182 | 183 | /** 184 | Use the provided Local to format a supplied NSDecimalNumber. 185 | 186 | - returns: a NSDecimalNumber -> String closure. 187 | */ 188 | static func formatted(withStyle style: NumberFormatter.Style, forLocale locale: MNYLocale) -> (NSDecimalNumber) -> String { 189 | let id = "\(locale.localeIdentifier)@currency=\(code)" 190 | let locale = NSLocale(localeIdentifier: NSLocale.canonicalLocaleIdentifier(from: id)) 191 | return formatted(withStyle: style, forLocale: locale) 192 | } 193 | } 194 | 195 | /** 196 | # Currency 197 | A namespace for currency related types. 198 | */ 199 | public struct Currency { 200 | 201 | /** 202 | 203 | # Currency.Base 204 | 205 | `Currency.Base` is a class which composes a number formatter 206 | and a locale. It doesn't conform to `CurrencyType` but it can 207 | be used as a base class for currency types which only require 208 | a shared instance. 209 | */ 210 | public class Base { 211 | 212 | public let _code: String 213 | public let _scale: Int 214 | public let _symbol: String? 215 | 216 | init(code: String, scale: Int, symbol: String?) { 217 | _code = code 218 | _scale = scale 219 | _symbol = symbol 220 | } 221 | 222 | convenience init(code: String) { 223 | let idFromComponents = NSLocale.localeIdentifier(fromComponents: [NSLocale.Key.currencyCode.rawValue: code]) 224 | let canonical = NSLocale.canonicalLocaleIdentifier(from: idFromComponents) 225 | let nslocale = NSLocale(localeIdentifier: canonical) 226 | let locale = Locale(identifier: canonical) 227 | let symbol = nslocale.mny_currencySymbol! 228 | 229 | let fmtr = NumberFormatter() 230 | fmtr.locale = locale 231 | fmtr.numberStyle = .currency 232 | fmtr.currencyCode = code 233 | fmtr.currencySymbol = nslocale.mny_currencySymbol 234 | 235 | let scale = fmtr.maximumFractionDigits 236 | self.init(code: code, scale: scale, symbol: symbol) 237 | } 238 | 239 | convenience init(locale: NSLocale) { 240 | let code = locale.mny_currencyCode! 241 | let symbol = locale.mny_currencySymbol 242 | 243 | let fmtr = NumberFormatter() 244 | fmtr.numberStyle = .currency 245 | fmtr.locale = Locale(identifier: locale.localeIdentifier) 246 | fmtr.currencyCode = code 247 | 248 | let scale = fmtr.maximumFractionDigits 249 | self.init(code: code, scale: scale, symbol: symbol) 250 | } 251 | } 252 | 253 | /** 254 | 255 | # Currency.Local 256 | 257 | `Currency.Local` is a `BaseCurrency` subclass which represents 258 | the device's current currency, using `NSLocale.currentLocale()`. 259 | */ 260 | public final class Local: Currency.Base, ISOCurrencyType { 261 | public static var sharedInstance = Local(locale: NSLocale.current as NSLocale) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /Sources/Decimal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | import ValueCoding 29 | 30 | /** 31 | # Decimal 32 | A value type which implements `DecimalNumberType` using `NSDecimalNumber` internally. 33 | 34 | It is generic over the decimal number behavior type, which defines the rounding 35 | and scale rules for base 10 decimal arithmetic. 36 | */ 37 | public struct _Decimal: DecimalNumberType { 38 | 39 | public typealias DecimalNumberBehavior = Behavior 40 | 41 | /// Access the underlying decimal storage. 42 | /// - returns: the `NSDecimalNumber` 43 | public let storage: NSDecimalNumber 44 | 45 | /** 46 | Initialize a new value using the underlying decimal storage. 47 | 48 | - parameter storage: a `NSDecimalNumber` defaults to zero. 49 | */ 50 | public init(storage: NSDecimalNumber = NSDecimalNumber.zero) { 51 | self.storage = storage 52 | } 53 | } 54 | 55 | // MARK: - Equality 56 | 57 | public func ==(lhs: _Decimal, rhs: _Decimal) -> Bool { 58 | return lhs.storage == rhs.storage 59 | } 60 | 61 | // MARK: - Comparable 62 | 63 | public func <(lhs: _Decimal, rhs: _Decimal) -> Bool { 64 | return lhs.storage < rhs.storage 65 | } 66 | 67 | /// `Decimal` with plain decimal number behavior 68 | public typealias PlainDecimal = _Decimal 69 | 70 | /// `BankersDecimal` with banking decimal number behavior 71 | public typealias BankersDecimal = _Decimal 72 | 73 | // MARK: - Value Coding 74 | 75 | extension _Decimal: ValueCoding { 76 | public typealias Coder = _DecimalCoder 77 | } 78 | 79 | /** 80 | Coding class to support `_Decimal` `ValueCoding` conformance. 81 | */ 82 | public final class _DecimalCoder: NSObject, NSCoding, CodingProtocol { 83 | 84 | public let value: _Decimal 85 | 86 | public required init(_ v: _Decimal) { 87 | value = v 88 | } 89 | 90 | public init?(coder aDecoder: NSCoder) { 91 | let storage = aDecoder.decodeObject(forKey: "storage") as! NSDecimalNumber 92 | value = _Decimal(storage: storage) 93 | } 94 | 95 | public func encode(with aCoder: NSCoder) { 96 | aCoder.encode(value.storage, forKey: "storage") 97 | } 98 | } 99 | 100 | 101 | -------------------------------------------------------------------------------- /Sources/DecimalNumberType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /** 30 | 31 | # DecimalNumberBehaviorType 32 | 33 | Defines the decimal number behavior, i.e. `NSDecimalNumberBehaviors` 34 | of the type. 35 | 36 | */ 37 | public protocol DecimalNumberBehaviorType { 38 | 39 | /// Specify the decimal number (i.e. rounding, scale etc) for base 10 calculations 40 | static var decimalNumberBehaviors: NSDecimalNumberBehaviors { get } 41 | } 42 | 43 | /** 44 | 45 | # DecimalNumberBehavior 46 | 47 | This is a name space of types which conform to `DecimalNumberBehaviorType` 48 | with common rounding modes. All have maximum precision, of 38 significant 49 | digits. 50 | */ 51 | public struct DecimalNumberBehavior { 52 | 53 | /// Plain rounding mode 54 | public struct Plain: DecimalNumberBehaviorType { 55 | public static let decimalNumberBehaviors = DecimalNumberBehavior.behavior(withMode: .plain) 56 | } 57 | 58 | /// Round down mode 59 | public struct RoundDown: DecimalNumberBehaviorType { 60 | public static let decimalNumberBehaviors = DecimalNumberBehavior.behavior(withMode: .down) 61 | } 62 | 63 | /// Round up mode 64 | public struct RoundUp: DecimalNumberBehaviorType { 65 | public static let decimalNumberBehaviors = DecimalNumberBehavior.behavior(withMode: .up) 66 | } 67 | 68 | /// Bankers rounding mode, see `NSRoundingMode.RoundBankers` for info. 69 | public struct Bankers: DecimalNumberBehaviorType { 70 | public static let decimalNumberBehaviors = DecimalNumberBehavior.behavior(withMode: .bankers) 71 | } 72 | 73 | private static func behavior(withMode mode: NSDecimalNumber.RoundingMode, scale: Int16 = 38) -> NSDecimalNumberBehaviors { 74 | return NSDecimalNumberHandler(roundingMode: mode, scale: 38, raiseOnExactness: false, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true) 75 | } 76 | } 77 | 78 | /** 79 | 80 | # DecimalNumberType 81 | 82 | A protocol which defines the necessary interface to support decimal number 83 | calculations and operators. 84 | */ 85 | public protocol DecimalNumberType: Hashable, SignedNumber, ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, CustomStringConvertible { 86 | 87 | associatedtype DecimalStorageType 88 | associatedtype DecimalNumberBehavior: DecimalNumberBehaviorType 89 | 90 | /// Access the underlying storage 91 | var storage: DecimalStorageType { get } 92 | 93 | /// Flag to indicate if the decimal number is less than zero 94 | var isNegative: Bool { get } 95 | 96 | /** 97 | Negates the receiver, equivalent to multiplying it by -1 98 | - returns: another instance of this type. 99 | */ 100 | var negative: Self { get } 101 | 102 | /// Access an integer value representation 103 | var integerValue: IntegerLiteralType { get } 104 | 105 | /// Access a float value representation 106 | var floatValue: FloatLiteralType { get } 107 | 108 | /** 109 | Initialize a new `DecimalNumberType` with the underlying storage. 110 | This is necessary in order to convert between different decimal number 111 | types. 112 | 113 | - parameter storage: the underlying decimal storage type 114 | e.g. NSDecimalNumber or NSDecimal 115 | */ 116 | init(storage: DecimalStorageType) 117 | 118 | /** 119 | Subtract a matching `DecimalNumberType` from the receiver. 120 | 121 | - parameter other: another instance of this type. 122 | - returns: another instance of this type. 123 | */ 124 | func subtracting(_: Self) -> Self 125 | 126 | /** 127 | Add a matching `DecimalNumberType` to the receiver. 128 | 129 | - parameter other: another instance of this type. 130 | - returns: another instance of this type. 131 | */ 132 | func adding(_: Self) -> Self 133 | 134 | /** 135 | Multiply the receive by 10^n 136 | 137 | - parameter n: an `Int` for the 10 power index 138 | - returns: another instance of this type. 139 | */ 140 | func multiplying(byPowerOf10: Int16) -> Self 141 | 142 | /** 143 | Multiply a matching `DecimalNumberType` with the receiver. 144 | 145 | - parameter other: another instance of this type. 146 | - returns: another instance of this type. 147 | */ 148 | func multiplying(by: Self) -> Self 149 | 150 | /** 151 | Multiply another `DecimalNumberType` with the receiver. The other 152 | `DecimalNumberType` must have the same underlying `DecimalStorageType` as 153 | this `DecimalNumberType`. 154 | 155 | - parameter other: another `DecimalNumberType` value of different type. 156 | - returns: a different `DecimalNumberType` value. 157 | */ 158 | func multiplying(by: Other) -> Other where Other.DecimalStorageType == DecimalStorageType 159 | 160 | /** 161 | Divide the receiver by a matching `DecimalNumberType`. 162 | 163 | - parameter other: another instance of this type. 164 | - returns: another instance of this type. 165 | */ 166 | func dividing(by: Self) -> Self 167 | 168 | /** 169 | Divide the receiver by another `DecimalNumberType`. The other 170 | `DecimalNumberType` must have the same underlying `DecimalStorageType` as 171 | this `DecimalNumberType`. 172 | 173 | - parameter other: another `DecimalNumberType` value of different type. 174 | - returns: another instance of this type. 175 | */ 176 | func dividing(by: Other) -> Other where Other.DecimalStorageType == DecimalStorageType 177 | 178 | /** 179 | The remainder of dividing another `DecimalNumberType` into the receiver. 180 | 181 | - parameter other: another instance of this type. 182 | - returns: another instance of this type. 183 | */ 184 | func remainder(_: Self) -> Self 185 | } 186 | 187 | // MARK: - Extensions 188 | 189 | /** 190 | Extensions on `DecimalNumberType` where the underlying storage type 191 | is `NSDecimalNumber`. 192 | */ 193 | public extension DecimalNumberType where DecimalStorageType == NSDecimalNumber { 194 | 195 | /// Flag to indicate if the decimal number is less than zero 196 | var isNegative: Bool { 197 | return storage.isNegative 198 | } 199 | 200 | /// The negative of Self. 201 | /// - returns: a `_Decimal` 202 | var negative: Self { 203 | return Self(storage: storage.negate(withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 204 | } 205 | 206 | /// Access an integer value representation 207 | var integerValue: Int { 208 | return storage.intValue 209 | } 210 | 211 | /// Access a float value representation 212 | var floatValue: Double { 213 | return storage.doubleValue 214 | } 215 | 216 | /// Text description. 217 | var description: String { 218 | return "\(storage.description)" 219 | } 220 | 221 | /// Hash value 222 | var hashValue: Int { 223 | return storage.hashValue 224 | } 225 | 226 | /// Initialize a new decimal with an `Int`. 227 | /// - parameter value: an `Int`. 228 | /// - returns: an initialized `DecimalNumberType`. 229 | init(_ value: Int) { 230 | switch value { 231 | case 0: 232 | self.init(storage: NSDecimalNumber.zero) 233 | case 1: 234 | self.init(storage: NSDecimalNumber.one) 235 | default: 236 | self.init(storage: NSDecimalNumber(integerLiteral: value).rounding(accordingToBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 237 | } 238 | } 239 | 240 | /// Initialize a new decimal with an `UInt8`. 241 | /// - parameter value: an `UInt8`. 242 | /// - returns: an initialized `DecimalNumberType`. 243 | init(_ value: UInt8) { 244 | self.init(Int(value)) 245 | } 246 | 247 | /// Initialize a new decimal with an `Int8`. 248 | /// - parameter value: an `Int8`. 249 | /// - returns: an initialized `DecimalNumberType`. 250 | init(_ value: Int8) { 251 | self.init(Int(value)) 252 | } 253 | 254 | /// Initialize a new decimal with an `UInt16`. 255 | /// - parameter value: an `UInt16`. 256 | /// - returns: an initialized `DecimalNumberType`. 257 | init(_ value: UInt16) { 258 | self.init(Int(value)) 259 | } 260 | 261 | /// Initialize a new decimal with an `Int16`. 262 | /// - parameter value: an `Int16`. 263 | /// - returns: an initialized `DecimalNumberType`. 264 | init(_ value: Int16) { 265 | self.init(Int(value)) 266 | } 267 | 268 | /// Initialize a new decimal with an `UInt32`. 269 | /// - parameter value: an `UInt32`. 270 | /// - returns: an initialized `DecimalNumberType`. 271 | init(_ value: UInt32) { 272 | self.init(Int(value)) 273 | } 274 | 275 | /// Initialize a new decimal with an `Int32`. 276 | /// - parameter value: an `Int32`. 277 | /// - returns: an initialized `DecimalNumberType`. 278 | init(_ value: Int32) { 279 | self.init(Int(value)) 280 | } 281 | 282 | /// Initialize a new decimal with an `UInt64`. 283 | /// - parameter value: an `UInt64`. 284 | /// - returns: an initialized `DecimalNumberType`. 285 | init(_ value: UInt64) { 286 | self.init(Int(value)) 287 | } 288 | 289 | /// Initialize a new decimal with an `Int64`. 290 | /// - parameter value: an `Int64`. 291 | /// - returns: an initialized `DecimalNumberType`. 292 | init(_ value: Int64) { 293 | self.init(Int(value)) 294 | } 295 | 296 | /** 297 | Initialize a new value using a `IntegerLiteralType` 298 | 299 | - parameter integerLiteral: a `IntegerLiteralType` for the system, probably `Int`. 300 | */ 301 | init(integerLiteral value: Swift.IntegerLiteralType) { 302 | self.init(value) 303 | } 304 | 305 | /// Initialize a new decimal with an `Double`. 306 | /// - parameter value: an `Double`. 307 | /// - returns: an initialized `DecimalNumberType`. 308 | init(_ value: Double) { 309 | self.init(storage: NSDecimalNumber(floatLiteral: value).rounding(accordingToBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 310 | } 311 | 312 | /// Initialize a new decimal with a `Float`. 313 | /// - parameter value: an `Float`. 314 | /// - returns: an initialized `DecimalNumberType`. 315 | init(_ value: Float) { 316 | self.init(Double(value)) 317 | } 318 | 319 | /** 320 | Initialize a new value using a `FloatLiteralType` 321 | 322 | - parameter floatLiteral: a `FloatLiteralType` for the system, probably `Double`. 323 | */ 324 | init(floatLiteral value: Swift.FloatLiteralType) { 325 | self.init(value) 326 | } 327 | 328 | /** 329 | Subtract a matching `DecimalNumberType` from the receiver. 330 | 331 | - parameter other: another instance of this type. 332 | - returns: another instance of this type. 333 | */ 334 | func subtracting(_ other: Self) -> Self { 335 | return Self(storage: storage.subtracting(other.storage, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 336 | } 337 | 338 | /** 339 | Add a matching `DecimalNumberType` from the receiver. 340 | 341 | - parameter other: another instance of this type. 342 | - returns: another instance of this type. 343 | */ 344 | func adding(_ other: Self) -> Self { 345 | return Self(storage: storage.adding(other.storage, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 346 | } 347 | 348 | /** 349 | Multiply the receive by 10^n 350 | 351 | - parameter n: an `Int` for the 10 power index 352 | - returns: another instance of this type. 353 | */ 354 | func multiplying(byPowerOf10 index: Int16) -> Self { 355 | return Self(storage: storage.multiplying(byPowerOf10: index, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 356 | } 357 | 358 | /** 359 | Multiply a matching `DecimalNumberType` with the receiver. 360 | 361 | - parameter other: another instance of this type. 362 | - returns: another instance of this type. 363 | */ 364 | func multiplying(by other: Self) -> Self { 365 | return Self(storage: storage.multiplying(by: other.storage, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 366 | } 367 | 368 | /** 369 | Multiply a different `DecimalNumberType` which also has `NSDecimalNumber` 370 | as the storage type with the receiver. 371 | 372 | - parameter other: another `DecimalNumberType` with `NSDecimalNumber` storage. 373 | - returns: another instance of this type. 374 | */ 375 | func multiplying(by other: Other) -> Other where Other.DecimalStorageType == NSDecimalNumber { 376 | return Other(storage: storage.multiplying(by: other.storage, withBehavior: Other.DecimalNumberBehavior.decimalNumberBehaviors) ) 377 | } 378 | 379 | /** 380 | Divide the receiver by another instance of this type. 381 | 382 | - parameter other: another instance of this type. 383 | - returns: another instance of this type. 384 | */ 385 | func dividing(by other: Self) -> Self { 386 | return Self(storage: storage.dividing(by: other.storage, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 387 | } 388 | 389 | /** 390 | Divide the receiver by a different `DecimalNumberType` which also has `NSDecimalNumber` 391 | as the storage type. 392 | 393 | - parameter other: another `DecimalNumberType` with `NSDecimalNumber` storage. 394 | - returns: another instance of this type. 395 | */ 396 | func dividing(by other: Other) -> Other where Other.DecimalStorageType == NSDecimalNumber { 397 | return Other(storage: storage.dividing(by: other.storage, withBehavior: Other.DecimalNumberBehavior.decimalNumberBehaviors)) 398 | } 399 | 400 | /** 401 | The remainder of dividing another instance of this type into the receiver. 402 | 403 | - parameter other: another instance of this type. 404 | - returns: another instance of this type. 405 | */ 406 | func remainder(_ other: Self) -> Self { 407 | return Self(storage: storage.remainder(other.storage, withBehavior: DecimalNumberBehavior.decimalNumberBehaviors)) 408 | } 409 | } 410 | 411 | extension DecimalNumberType where Self.IntegerLiteralType == Int { 412 | 413 | /// Get the reciprocal of the receiver. 414 | public var reciprocal: Self { 415 | return Self(integerLiteral: 1).dividing(by: self) 416 | } 417 | } 418 | 419 | 420 | // MARK: - Operators 421 | 422 | // MARK: - Subtraction 423 | 424 | public func -(lhs: T, rhs: T) -> T { 425 | return lhs.subtracting(rhs) 426 | } 427 | 428 | public func -(lhs: T, rhs: T.IntegerLiteralType) -> T { 429 | return lhs - T(integerLiteral: rhs) 430 | } 431 | 432 | public func -(lhs: T.IntegerLiteralType, rhs: T) -> T { 433 | return T(integerLiteral: lhs) - rhs 434 | } 435 | 436 | public func -(lhs: T, rhs: T.FloatLiteralType) -> T { 437 | return lhs - T(floatLiteral: rhs) 438 | } 439 | 440 | public func -(lhs: T.FloatLiteralType, rhs: T) -> T { 441 | return T(floatLiteral: lhs) - rhs 442 | } 443 | 444 | // MARK: - Addition 445 | 446 | public func +(lhs: T, rhs: T) -> T { 447 | return lhs.adding(rhs) 448 | } 449 | 450 | public func +(lhs: T, rhs: T.IntegerLiteralType) -> T { 451 | return lhs + T(integerLiteral: rhs) 452 | } 453 | 454 | public func +(lhs: T.IntegerLiteralType, rhs: T) -> T { 455 | return T(integerLiteral: lhs) + rhs 456 | } 457 | 458 | public func +(lhs: T, rhs: T.FloatLiteralType) -> T { 459 | return lhs + T(floatLiteral: rhs) 460 | } 461 | 462 | public func +(lhs: T.FloatLiteralType, rhs: T) -> T { 463 | return T(floatLiteral: lhs) + rhs 464 | } 465 | 466 | // MARK: - Multiplication 467 | 468 | public func *(lhs: T, rhs: T) -> T { 469 | return lhs.multiplying(by: rhs) 470 | } 471 | 472 | public func *(lhs: T, rhs: T.IntegerLiteralType) -> T { 473 | return lhs * T(integerLiteral: rhs) 474 | } 475 | 476 | public func *(lhs: T, rhs: T.FloatLiteralType) -> T { 477 | return lhs * T(floatLiteral: rhs) 478 | } 479 | 480 | public func *(lhs: T.IntegerLiteralType, rhs: T) -> T { 481 | return rhs * lhs 482 | } 483 | 484 | public func *(lhs: T.FloatLiteralType, rhs: T) -> T { 485 | return rhs * lhs 486 | } 487 | 488 | public func *(lhs: T, rhs: V) -> V where T: DecimalNumberType, V: DecimalNumberType, T.DecimalStorageType == V.DecimalStorageType { 489 | return lhs.multiplying(by: rhs) 490 | } 491 | 492 | // MARK: - Division 493 | 494 | public func /(lhs: T, rhs: T) -> T { 495 | return lhs.dividing(by: rhs) 496 | } 497 | 498 | public func /(lhs: T, rhs: T.IntegerLiteralType) -> T { 499 | return lhs / T(integerLiteral: rhs) 500 | } 501 | 502 | public func /(lhs: T, rhs: T.FloatLiteralType) -> T { 503 | return lhs / T(floatLiteral: rhs) 504 | } 505 | 506 | public func /(lhs: T, rhs: V) -> V where T: DecimalNumberType, V: DecimalNumberType, T.DecimalStorageType == V.DecimalStorageType { 507 | return lhs.dividing(by: rhs) 508 | } 509 | 510 | // MARK: - Remainder 511 | 512 | public func %(lhs: T, rhs: T) -> T { 513 | return lhs.remainder(rhs) 514 | } 515 | 516 | 517 | 518 | -------------------------------------------------------------------------------- /Sources/Locale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /** 30 | LanguageType provides an interface to retrieve 31 | a language identifier. 32 | */ 33 | public protocol LanguageType { 34 | 35 | /// - returns: the language identifier as a String 36 | var languageIdentifier: String { get } 37 | } 38 | 39 | /** 40 | CountryType provides an interface to retrieve 41 | a country identifier. 42 | */ 43 | public protocol CountryType { 44 | 45 | /// - returns: the country identifier as a String 46 | var countryIdentifier: String { get } 47 | } 48 | 49 | /** 50 | LocaleType provides an interface to retrieve 51 | a locale identifier. 52 | */ 53 | public protocol LocaleType { 54 | 55 | /// - returns: the locale identifier as a String 56 | var localeIdentifier: String { get } 57 | } 58 | 59 | /** 60 | LocaleType extension for types which also conform to 61 | LanguageType and CountryType. 62 | */ 63 | extension LocaleType where Self: LanguageType, Self: CountryType { 64 | 65 | /** 66 | Default implementation of localeIdentifier, where 67 | if a country identifier is not empty, it is appended to the 68 | language identifier, with an underscore. 69 | - returns: the locale identifier as a String 70 | */ 71 | public var localeIdentifier: String { 72 | guard !countryIdentifier.isEmpty else { 73 | return languageIdentifier 74 | } 75 | return "\(languageIdentifier)_\(countryIdentifier)" 76 | } 77 | } 78 | 79 | /** 80 | Convenience currency related properties on NSLocale 81 | */ 82 | internal extension NSLocale { 83 | 84 | /// - returns: a String? for the currency code. 85 | var mny_currencyCode: String? { 86 | if #available(iOS 10.0, iOSApplicationExtension 10.0, watchOS 3.0, watchOSApplicationExtension 3.0, tvOS 10.0, tvOSApplicationExtension 10.0, OSX 10.12, OSXApplicationExtension 10.12, *) { 87 | return currencyCode 88 | } else { 89 | return object(forKey: NSLocale.Key.currencyCode) as? String 90 | } 91 | } 92 | 93 | /// - returns: a String? for the currency symbol. 94 | var mny_currencySymbol: String? { 95 | if #available(iOS 10.0, iOSApplicationExtension 10.0, watchOS 3.0, watchOSApplicationExtension 3.0, tvOS 10.0, tvOSApplicationExtension 10.0, OSX 10.12, OSXApplicationExtension 10.12, *) { 96 | return currencySymbol 97 | } else { 98 | return object(forKey: NSLocale.Key.currencySymbol) as? String 99 | } 100 | } 101 | 102 | /// - returns: a String? for the currency grouping separator. 103 | var mny_currencyGroupingSeparator: String? { 104 | return object(forKey: NSLocale.Key.groupingSeparator) as? String 105 | } 106 | 107 | /// - returns: a String? for the currency decimal separator. 108 | var mny_currencyDecimalSeparator: String? { 109 | return object(forKey: NSLocale.Key.decimalSeparator) as? String 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /Sources/Money.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import Foundation 28 | import ValueCoding 29 | 30 | /** 31 | 32 | # MoneyType 33 | 34 | `MoneyType` is a protocol which refines `DecimalNumberType`. It 35 | adds a generic type for the currency. 36 | */ 37 | public protocol MoneyType: DecimalNumberType, ValueCoding { 38 | associatedtype Currency: CurrencyType 39 | 40 | /// Access the underlying decimal 41 | var decimal: _Decimal { get } 42 | 43 | /// Access the underlying minor units 44 | var minorUnits: IntegerLiteralType { get } 45 | 46 | /// Initialize the money with a decimal 47 | init(_: _Decimal) 48 | 49 | /// Initialize the money with a integer representing minor units 50 | init(minorUnits: IntegerLiteralType) 51 | } 52 | 53 | public extension MoneyType { 54 | 55 | /// - returns: a String for the currency's international code. 56 | var currencyCode: String { 57 | return Currency.code 58 | } 59 | 60 | /// - returns: a String for the currency's symbol in the current locale. 61 | var currencySymbol: String? { 62 | return Currency.symbol 63 | } 64 | } 65 | 66 | public extension MoneyType where DecimalStorageType == NSDecimalNumber { 67 | 68 | /// Convenience access to the "amount" as an NSDecimalNumber. 69 | var amount: DecimalStorageType { 70 | return storage 71 | } 72 | 73 | /** 74 | 75 | ### Formatted String 76 | 77 | This function will format the Money type into a string for 78 | the current locale. 79 | 80 | For custom currencies which define their own currency code, 81 | create a lazy static `NSNumberFormatter`. Set the following 82 | properties on it: `currencySymbol`, `internationalCurrencySymbol` 83 | `currencyGroupingSeparator` and `currencyDecimalSeparator`, as they 84 | will be used when formatting the string. Feel free to fall back to 85 | the current locale's values for any of these to maintain 86 | natural looking formatting. See the example project for more. 87 | 88 | - parameter style: the `NSNumberFormatterStyle` to use. 89 | - returns: a localized and formatted string for the money amount. 90 | */ 91 | func formatted(withStyle style: NumberFormatter.Style) -> String { 92 | return Currency.formatted(withStyle: style, forLocaleId: NSLocale.current.identifier)(amount) 93 | } 94 | 95 | /** 96 | 97 | ### Formatted String for specific Locale 98 | 99 | This function will format the Money type into a string suitable 100 | for a specific local. It accepts an parameter for the 101 | style `NSNumberFormatterStyle`. Note that iOS 9 and OS X 10.11 102 | added new styles which are relevant for currency. 103 | 104 | These are `.CurrencyISOCodeStyle`, `.CurrencyPluralStyle`, and 105 | `.CurrencyAccountingStyle`. 106 | 107 | - parameter style: the `NSNumberFormatterStyle` to use. 108 | - parameter locale: a `MNYLocale` value 109 | - returns: a localized and formatted string for the money amount. 110 | */ 111 | func formatted(withStyle style: NumberFormatter.Style, forLocale locale: MNYLocale) -> String { 112 | return Currency.formatted(withStyle: style, forLocale: locale)(amount) 113 | } 114 | } 115 | 116 | public extension MoneyType where DecimalStorageType == BankersDecimal.DecimalStorageType { 117 | 118 | /** 119 | Use a `BankersDecimal` to convert the receive into another `MoneyType`. To use this 120 | API the underlying `DecimalStorageType`s between the receiver, the other `MoneyType` 121 | must both be the same a that of `BankersDecimal` (which luckily they are). 122 | 123 | - parameter rate: a `BankersDecimal` representing the rate. 124 | - returns: another `MoneyType` value. 125 | */ 126 | func convert(withRate rate: BankersDecimal) -> Other where Other.DecimalStorageType == BankersDecimal.DecimalStorageType { 127 | return multiplying(by: Other(storage: rate.storage)) 128 | } 129 | } 130 | 131 | /** 132 | 133 | # Money 134 | 135 | `_Money` is a value type, conforming to `MoneyType`, which is generic over the currency type. 136 | 137 | To work in whatever the local currency is, use `Money`. It should not 138 | be necessary to use `_Money` directly, instead, use a typealias, 139 | such as `USD` or `GBP`. 140 | */ 141 | public struct _Money: MoneyType { 142 | 143 | public typealias DecimalNumberBehavior = C 144 | public typealias Currency = C 145 | 146 | /// Access the underlying decimal. 147 | /// - returns: the `_Decimal` 148 | public let decimal: _Decimal 149 | 150 | /// Access the underlying minor units 151 | /// - returns: the `IntegerLiteralType` minor units 152 | public var minorUnits: IntegerLiteralType { 153 | return decimal.multiplying(byPowerOf10: Int16(Currency.scale)).integerValue 154 | } 155 | 156 | /// Access the underlying decimal storage. 157 | /// - returns: the `_Decimal.DecimalStorageType` 158 | public var storage: _Decimal.DecimalStorageType { 159 | return decimal.storage 160 | } 161 | 162 | /// Flag to indicate if the decimal number is less than zero 163 | public var isNegative: Bool { 164 | return decimal.isNegative 165 | } 166 | 167 | /// The negative of Self. 168 | /// - returns: a `_Money` 169 | public var negative: _Money { 170 | return _Money(decimal.negative) 171 | } 172 | 173 | /** 174 | Initialize a new value using an underlying decimal. 175 | 176 | - parameter value: a `_Decimal` defaults to zero. 177 | */ 178 | public init(_ value: _Decimal = _Decimal()) { 179 | decimal = value 180 | } 181 | 182 | /** 183 | Initialize the money with a integer representing minor units. 184 | 185 | - parameter minorUnits: a `IntegerLiteralType` 186 | */ 187 | public init(minorUnits: IntegerLiteralType) { 188 | decimal = _Decimal(integerLiteral: minorUnits).multiplying(byPowerOf10: Int16(Currency.scale * -1)) 189 | } 190 | 191 | /** 192 | Initialize a new value using the underlying decimal storage. 193 | At the moment, this is a `NSDecimalNumber`. 194 | 195 | - parameter storage: a `_Decimal.DecimalStorageType` 196 | */ 197 | public init(storage: _Decimal.DecimalStorageType) { 198 | decimal = _Decimal(storage: storage) 199 | } 200 | 201 | /** 202 | Initialize a new value using a `IntegerLiteralType` 203 | 204 | - parameter integerLiteral: a `IntegerLiteralType` for the system, probably `Int`. 205 | */ 206 | public init(integerLiteral value: IntegerLiteralType) { 207 | decimal = _Decimal(integerLiteral: value) 208 | } 209 | 210 | /** 211 | Initialize a new value using a `FloatLiteralType` 212 | 213 | - parameter floatLiteral: a `FloatLiteralType` for the system, probably `Double`. 214 | */ 215 | public init(floatLiteral value: FloatLiteralType) { 216 | decimal = _Decimal(floatLiteral: value) 217 | } 218 | 219 | /** 220 | Subtract a matching `_Money` from the receiver. 221 | 222 | - parameter other: another instance of this type. 223 | - returns: another instance of this type. 224 | */ 225 | public func subtracting(_ other: _Money) -> _Money { 226 | return _Money(decimal.subtracting(other.decimal)) 227 | } 228 | 229 | /** 230 | Add a matching `_Money` from the receiver. 231 | 232 | - parameter other: another instance of this type. 233 | - returns: another instance of this type. 234 | */ 235 | public func adding(_ other: _Money) -> _Money { 236 | return _Money(decimal.adding(other.decimal)) 237 | } 238 | 239 | /** 240 | Multiply a matching `_Money` from the receiver. 241 | 242 | - parameter other: another instance of this type. 243 | - returns: another instance of this type. 244 | */ 245 | public func multiplying(by other: _Money) -> _Money { 246 | return _Money(decimal.multiplying(by: other.decimal)) 247 | } 248 | 249 | /** 250 | Divide a matching `_Money` from the receiver. 251 | 252 | - parameter other: another instance of this type. 253 | - returns: another instance of this type. 254 | */ 255 | public func dividing(by other: _Money) -> _Money { 256 | return _Money(decimal.dividing(by: other.decimal)) 257 | } 258 | 259 | /** 260 | The remainder of dividing another `_Money` into the receiver. 261 | 262 | - parameter other: another instance of this type. 263 | - returns: another instance of this type. 264 | */ 265 | public func remainder(_ other: _Money) -> _Money { 266 | return _Money(decimal.remainder(other.decimal)) 267 | } 268 | } 269 | 270 | // MARK: - Equality 271 | 272 | public func ==(lhs: _Money, rhs: _Money) -> Bool { 273 | return lhs.decimal == rhs.decimal 274 | } 275 | 276 | // MARK: - Comparable 277 | 278 | public func <(lhs: _Money, rhs: _Money) -> Bool { 279 | return lhs.decimal < rhs.decimal 280 | } 281 | 282 | // MARK: - CustomStringConvertible 283 | 284 | extension _Money: CustomStringConvertible { 285 | 286 | /** 287 | Returns the result of the `formatted` function using 288 | NSNumberFormatterStyle.CurrencyStyle. 289 | */ 290 | public var description: String { 291 | return formatted(withStyle: C.defaultFormattingStyle) 292 | } 293 | } 294 | 295 | // MARK: - Value Coding 296 | 297 | extension _Money: ValueCoding { 298 | public typealias Coder = _MoneyCoder 299 | } 300 | 301 | /** 302 | Coding class to support `_Decimal` `ValueCoding` conformance. 303 | */ 304 | public final class _MoneyCoder: NSObject, NSCoding, CodingProtocol { 305 | 306 | public let value: _Money 307 | 308 | public required init(_ v: _Money) { 309 | value = v 310 | } 311 | 312 | public init?(coder aDecoder: NSCoder) { 313 | let decimal = _Decimal.decode(aDecoder.decodeObject(forKey: "decimal") as AnyObject?) 314 | value = _Money(decimal!) 315 | } 316 | 317 | public func encode(with aCoder: NSCoder) { 318 | aCoder.encode(value.decimal.encoded, forKey: "decimal") 319 | } 320 | } 321 | 322 | 323 | // MARK: - Consumption Types 324 | 325 | /// The current locale money 326 | public typealias Money = _Money 327 | -------------------------------------------------------------------------------- /Sources/NSDecimalExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | 28 | import Foundation 29 | 30 | /** 31 | # Decimal Extension 32 | 33 | This is an extension on Decimal to support `DecimalNumberType` and 34 | `_Decimal`. 35 | */ 36 | internal extension Decimal { 37 | 38 | /** 39 | Static function to get the `NSDecimal` representation of 40 | zero. 41 | 42 | - returns: a `NSDecimal` of zero. 43 | */ 44 | static var zero: Decimal { 45 | return NSDecimalNumber.zero.decimalValue 46 | } 47 | 48 | /** 49 | Static function to get the `NSDecimal` representation of 50 | positive one. 51 | 52 | - returns: a `NSDecimal` of one. 53 | */ 54 | static var one: Decimal { 55 | return NSDecimalNumber.one.decimalValue 56 | } 57 | 58 | /** 59 | Boolean flag to indicate if the receive is a negative 60 | number. 61 | 62 | - returns: a `Bool` if the value is below zero. 63 | */ 64 | var isNegative: Bool { 65 | return self < Decimal.zero 66 | } 67 | 68 | internal init() { 69 | self = Decimal.zero 70 | } 71 | 72 | /** 73 | Subtract a `NSDecimal` from the receiver. 74 | 75 | - parameter other: another `NSDecimal`. 76 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 77 | - returns: a `NSDecimal`. 78 | */ 79 | func subtracting(_ rhs: Decimal, withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 80 | var (lhs, rhs) = (self, rhs) 81 | var result = Decimal() 82 | NSDecimalSubtract(&result, &lhs, &rhs, roundingMode) 83 | return result 84 | } 85 | 86 | /** 87 | Add a `NSDecimal` to the receiver. 88 | 89 | - parameter other: another `NSDecimal`. 90 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 91 | - returns: a `NSDecimal`. 92 | */ 93 | func adding(_ rhs: Decimal, withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 94 | var (lhs, rhs) = (self, rhs) 95 | var result = Decimal() 96 | NSDecimalAdd(&result, &lhs, &rhs, roundingMode) 97 | return result 98 | } 99 | 100 | /** 101 | Multiply a `NSDecimal` with the receiver. 102 | 103 | - parameter other: another `NSDecimal`. 104 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 105 | - returns: a `NSDecimal`. 106 | */ 107 | func multiplying(by rhs: Decimal, withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 108 | var (lhs, rhs) = (self, rhs) 109 | var result = Decimal() 110 | NSDecimalMultiply(&result, &lhs, &rhs, roundingMode) 111 | return result 112 | } 113 | 114 | /** 115 | Divide the receiver by a matching `NSDecimal`. 116 | 117 | - parameter other: another `NSDecimal`. 118 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 119 | - returns: a `NSDecimal`. 120 | */ 121 | func dividing(by rhs: Decimal, withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 122 | var (lhs, rhs) = (self, rhs) 123 | var result = Decimal() 124 | NSDecimalDivide(&result, &lhs, &rhs, roundingMode) 125 | return result 126 | } 127 | 128 | /** 129 | Calculates the negative of the receiver. 130 | 131 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 132 | - returns: a `NSDecimal`. 133 | */ 134 | func negate(withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 135 | let zero = Decimal.zero 136 | let negativeOne = zero.subtracting(Decimal.one, withRoundingMode: roundingMode) 137 | let result = multiplying(by: negativeOne, withRoundingMode: roundingMode) 138 | return result 139 | } 140 | 141 | /** 142 | The remainder of dividing another `NSDecimal` into the receiver. 143 | 144 | - parameter other: another `NSDecimal`. 145 | - parameter roundingMode: the NSRoundingMode to use for the calculation. 146 | - returns: a `NSDecimal`. 147 | */ 148 | func remainder(_ _rhs: Decimal, withRoundingMode roundingMode: NSDecimalNumber.RoundingMode) -> Decimal { 149 | let lhs = NSDecimalNumber(decimal: self) 150 | let rhs = NSDecimalNumber(decimal: _rhs) 151 | let behaviors = NSDecimalNumberHandler(roundingMode: roundingMode, scale: 38, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true) 152 | let result = lhs.remainder(rhs, withBehavior: behaviors) 153 | return result.decimalValue 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Sources/NSDecimalNumberExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | 28 | import Foundation 29 | 30 | // MARK: - Equality 31 | 32 | public func ==(lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> Bool { 33 | return lhs.isEqual(to: rhs) 34 | } 35 | 36 | // MARK: - Comparable 37 | 38 | extension NSDecimalNumber: Comparable { } 39 | 40 | public func <(lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> Bool { 41 | return lhs.compare(rhs) == .orderedAscending 42 | } 43 | 44 | /** 45 | # NSDecimalNumber Extension 46 | This is an extension on NSDecimalNumber to support `DecimalNumberType` and 47 | `Decimal`. 48 | 49 | Note that NSDecimalNumber cannot conform to `DecimalNumberType` directly 50 | because it is a framework class which cannot be made final, and the protocol 51 | has functions which return Self. 52 | */ 53 | internal extension NSDecimalNumber { 54 | 55 | var isNegative: Bool { 56 | return self < NSDecimalNumber.zero 57 | } 58 | 59 | /** 60 | Calculates the negative of the receiver. 61 | 62 | - parameter behaviors: an optional NSDecimalNumberBehaviors? 63 | - returns: a `NSDecimalNumber`. 64 | */ 65 | func negate(withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { 66 | let negativeOne = NSDecimalNumber(mantissa: 1, exponent: 0, isNegative: true) 67 | let result = multiplying(by: negativeOne, withBehavior: behavior) 68 | return result 69 | } 70 | 71 | /** 72 | The remainder of dividing another `NSDecimalNumber` into the receiver. 73 | 74 | - parameter other: another `NSDecimalNumber`. 75 | - parameter behaviors: an optional NSDecimalNumberBehaviors? 76 | - returns: a `NSDecimalNumber`. 77 | */ 78 | func remainder(_ other: NSDecimalNumber, withBehavior behavior: NSDecimalNumberBehaviors?) -> NSDecimalNumber { 79 | let a: Int = (isNegative ? 0 : 1) 80 | let b: Int = (other.isNegative ? 0 : 1) 81 | let trueOrFalse = (a ^ b) > 0 ? true : false 82 | let roundingMode: NSDecimalNumber.RoundingMode = trueOrFalse ? NSDecimalNumber.RoundingMode.up : NSDecimalNumber.RoundingMode.down 83 | let roundingBehaviors = NSDecimalNumberHandler(roundingMode: roundingMode, scale: 0, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false) 84 | let quotient = dividing(by: other, withBehavior: roundingBehaviors) 85 | let toSubtract = quotient.multiplying(by: other, withBehavior: behavior) 86 | let result = subtracting(toSubtract, withBehavior: behavior) 87 | return result 88 | } 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /Supporting Files/Carthage.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // FeatureKit 3 | // 4 | // Copyright © 2016 FeatureKit. All rights reserved. 5 | // 6 | 7 | FRAMEWORK_SEARCH_PATHS[sdk=macosx*] = $(SRCROOT)/Carthage/Build/Mac/ $(inherited) 8 | FRAMEWORK_SEARCH_PATHS[sdk=iphone*] = $(SRCROOT)/Carthage/Build/iOS/ $(inherited) 9 | FRAMEWORK_SEARCH_PATHS[sdk=watch*] = $(SRCROOT)/Carthage/Build/watchOS/ $(inherited) 10 | FRAMEWORK_SEARCH_PATHS[sdk=appletv*] = $(SRCROOT)/Carthage/Build/tvOS/ $(inherited) 11 | -------------------------------------------------------------------------------- /Supporting Files/Generate.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env xcrun -sdk macosx swift 2 | 3 | // 4 | // Generate.swift 5 | // Money 6 | // 7 | // Created by Daniel Thorpe on 01/11/2015. 8 | // 9 | // 10 | 11 | import Foundation 12 | 13 | typealias Writer = (String) -> Void 14 | typealias Generator = (Writer) -> Void 15 | 16 | let enUS = NSLocale(localeIdentifier: "en_US") 17 | 18 | protocol TypeGenerator { 19 | static var typeName: String { get } 20 | var displayName: String { get } 21 | } 22 | 23 | extension TypeGenerator { 24 | 25 | var name: String { 26 | return displayName.capitalized(with: Locale(identifier: "en_US")) 27 | .replacingOccurrences(of: " ", with: "") 28 | .replacingOccurrences(of: " ", with: "") 29 | .replacingOccurrences(of: "-", with: "") 30 | .replacingOccurrences(of: "ʼ", with: "") 31 | .replacingOccurrences(of: ".", with: "") 32 | .replacingOccurrences(of: "&", with: "") 33 | .replacingOccurrences(of: "(", with: "") 34 | .replacingOccurrences(of: ")", with: "") 35 | .replacingOccurrences(of: "’", with: "") 36 | } 37 | 38 | var caseNameValue: String { 39 | return ".\(name)" 40 | } 41 | 42 | var protocolName: String { 43 | return "\(name)\(Self.typeName)Type" 44 | } 45 | } 46 | 47 | /// MARK: - Currency Info 48 | 49 | func createMoneyType(forCurrencyCode code: String) -> String { 50 | return "_Money" 51 | } 52 | 53 | func createExtension(for typename: String, withWriter writer: Writer, content: Generator) { 54 | writer("extension \(typename) {") 55 | content(writer) 56 | writer("}") 57 | } 58 | 59 | func createFrontMatter(withWriter writer: Writer) { 60 | writer("// ") 61 | writer("// Money, https://github.com/danthorpe/Money") 62 | writer("// Created by Dan Thorpe, @danthorpe") 63 | writer("// ") 64 | writer("// The MIT License (MIT)") 65 | writer("// ") 66 | writer("// Copyright (c) 2015 Daniel Thorpe") 67 | writer("// ") 68 | writer("// Permission is hereby granted, free of charge, to any person obtaining a copy") 69 | writer("// of this software and associated documentation files (the \"Software\"), to deal") 70 | writer("// in the Software without restriction, including without limitation the rights") 71 | writer("// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell") 72 | writer("// copies of the Software, and to permit persons to whom the Software is") 73 | writer("// furnished to do so, subject to the following conditions:") 74 | writer("// ") 75 | writer("// The above copyright notice and this permission notice shall be included in all") 76 | writer("// copies or substantial portions of the Software.") 77 | writer("// ") 78 | writer("// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR") 79 | writer("// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,") 80 | writer("// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE") 81 | writer("// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER") 82 | writer("// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,") 83 | writer("// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE") 84 | writer("// SOFTWARE.") 85 | writer("// ") 86 | writer("// Autogenerated from build scripts, do not manually edit this file.") 87 | writer("") 88 | } 89 | 90 | func createCurrencyTypes(line: Writer) { 91 | for code in Locale.isoCurrencyCodes { 92 | line("") 93 | line(" /// Currency \(code)") 94 | line(" public final class \(code): Currency.Base, ISOCurrencyType {") 95 | line(" /// - returns: lazy shared instance for Currency.\(code)") 96 | line(" public static var sharedInstance = \(code)(code: \"\(code)\")") 97 | line(" }") 98 | } 99 | } 100 | 101 | func createMoneyTypes(withWriter writer: Writer) { 102 | writer("") 103 | 104 | for code in Locale.isoCurrencyCodes { 105 | writer("/// \(code) Money") 106 | let name = createMoneyType(forCurrencyCode: code) 107 | writer("public typealias \(code) = \(name)") 108 | } 109 | } 110 | 111 | /// MARK: - Locale Info 112 | 113 | struct Country: Comparable, TypeGenerator, CustomStringConvertible { 114 | static let typeName = "Country" 115 | let id: String 116 | let displayName: String 117 | var langaugeIds = Set() 118 | 119 | var description: String { 120 | return "\(self.id): \(displayName) -> \(langaugeIds)" 121 | } 122 | 123 | init?(id: String) { 124 | self.id = id 125 | guard let countryDisplayName = enUS.displayName(forKey: NSLocale.Key.countryCode, value: id) else { 126 | return nil 127 | } 128 | displayName = countryDisplayName 129 | } 130 | } 131 | 132 | struct Language: Comparable, TypeGenerator, CustomStringConvertible { 133 | static let typeName = "Language" 134 | 135 | let id: String 136 | let displayName: String 137 | var countryIds = Set() 138 | 139 | var description: String { 140 | return "\(id): \(displayName) -> \(countryIds)" 141 | } 142 | 143 | var languageSpeakingCountryEnumName: String { 144 | return "\(name)Speaking\(Country.typeName)" 145 | } 146 | 147 | init?(id: String) { 148 | self.id = id 149 | guard let languageDisplayName = enUS.displayName(forKey: NSLocale.Key.languageCode, value: id) else { 150 | return nil 151 | } 152 | displayName = languageDisplayName 153 | } 154 | } 155 | 156 | func ==(lhs: Country, rhs: Country) -> Bool { 157 | return lhs.id == rhs.id 158 | } 159 | 160 | func <(lhs: Country, rhs: Country) -> Bool { 161 | return lhs.name < rhs.name 162 | } 163 | 164 | func ==(lhs: Language, rhs: Language) -> Bool { 165 | return lhs.id == rhs.id 166 | } 167 | 168 | func <(lhs: Language, rhs: Language) -> Bool { 169 | return lhs.name < rhs.name 170 | } 171 | 172 | typealias LanguagesById = Dictionary 173 | typealias CountriesById = Dictionary 174 | 175 | struct LocaleInfo { 176 | 177 | let languagesById: LanguagesById 178 | let countriesById: CountriesById 179 | let languages: [Language] 180 | let countries: [Country] 181 | let languagesWithLessThanTwoCountries: [Language] 182 | let languagesWithMoreThanOneCountry: [Language] 183 | 184 | init() { 185 | let localeIDs = NSLocale.availableLocaleIdentifiers 186 | var _countriesById = CountriesById() 187 | var _languagesById = LanguagesById() 188 | 189 | for id in localeIDs { 190 | let locale = NSLocale(localeIdentifier: id) 191 | let countryId = locale.object(forKey: NSLocale.Key.countryCode) as? String 192 | let country: Country? = countryId.flatMap { _countriesById[$0] ?? Country(id: $0) } 193 | let languageId = locale.object(forKey: NSLocale.Key.languageCode) as? String 194 | let language: Language? = languageId.flatMap { _languagesById[$0] ?? Language(id: $0) } 195 | 196 | if let countryId = countryId, var language = language { 197 | language.countryIds.insert(countryId) 198 | _languagesById.updateValue(language, forKey: language.id) 199 | } 200 | 201 | if let languageId = languageId, var country = country { 202 | country.langaugeIds.insert(languageId) 203 | _countriesById.updateValue(country, forKey: country.id) 204 | } 205 | } 206 | 207 | self.languagesById = _languagesById 208 | self.countriesById = _countriesById 209 | 210 | countries = ([Country])(countriesById.values).sorted { $0.langaugeIds.count > $1.langaugeIds.count } 211 | languages = ([Language])(languagesById.values).sorted { $0.countryIds.count > $1.countryIds.count } 212 | 213 | languagesWithLessThanTwoCountries = languages.filter({ $0.countryIds.count < 2 }).sorted() 214 | languagesWithMoreThanOneCountry = languages.filter({ $0.countryIds.count > 1 }).sorted() 215 | } 216 | } 217 | 218 | let info = LocaleInfo() 219 | 220 | func createLanguageSpeakingCountry(withWriter writer: Writer, language: Language) { 221 | let name = language.languageSpeakingCountryEnumName 222 | 223 | writer("") 224 | writer("/**") 225 | writer(" An enum of countries which speak \(language.displayName).") 226 | writer("*/") 227 | writer("public enum \(name): CountryType {") 228 | 229 | let _countries = language.countryIds.sorted().flatMap { info.countriesById[$0] } 230 | 231 | // Write the cases 232 | writer("") 233 | for country in _countries { 234 | writer(" /// \(country.displayName) is a country which speaks \(language.displayName).") 235 | writer(" case \(country.name)") 236 | } 237 | 238 | 239 | // Write a static constant for all 240 | let caseNames = _countries.map { $0.caseNameValue } 241 | let joinedCaseNames = caseNames.joined(separator: ", ") 242 | writer("") 243 | writer(" /// - returns: an Array of all the countries which speak \(language.displayName)") 244 | writer(" public static let all: [\(name)] = [ \(joinedCaseNames) ]") 245 | 246 | writer("") 247 | writer(" /// - returns: the country identifier of a specific \(language.displayName) speaking country.") 248 | writer(" public var countryIdentifier: String {") 249 | writer(" switch self {") 250 | 251 | for country in _countries { 252 | writer(" case .\(country.name):") 253 | writer(" return \"\(country.id)\"") 254 | } 255 | 256 | writer(" }") // End of switch 257 | writer(" }") // End of var 258 | 259 | writer("}") // End of enum 260 | } 261 | 262 | func createLanguageSpeakingCountries(withWriter writer: Writer) { 263 | writer("") 264 | writer("// MARK: - Country Types") 265 | for language in info.languagesWithMoreThanOneCountry { 266 | createLanguageSpeakingCountry(withWriter: writer, language: language) 267 | } 268 | } 269 | 270 | func createLocale(withWriter writer: Writer) { 271 | writer("") 272 | writer("// MARK: - Locale") 273 | 274 | do { 275 | writer("") 276 | writer("/**") 277 | writer("") 278 | writer("Locale is an enum for type safe representation ") 279 | writer("of locale identifiers. Its cases are languages ") 280 | writer("in US English. For languages which are spoken ") 281 | writer("in more than one country, an associated value ") 282 | writer("of the country should be provided. For example ") 283 | writer("") 284 | writer("```swift") 285 | writer("let locale: MNYLocale = .French(.France)") 286 | writer("```") 287 | writer("*/") 288 | writer("public enum MNYLocale {") 289 | 290 | for language in info.languages.sorted() { 291 | writer("") 292 | if language.countryIds.count > 1 { 293 | writer(" /**") 294 | writer(" ### \(language.displayName)") 295 | writer(" - requires: \(language.languageSpeakingCountryEnumName)") 296 | writer(" */") 297 | writer(" case \(language.name)(\(language.languageSpeakingCountryEnumName))") 298 | } 299 | else { 300 | writer(" /// ### \(language.displayName)") 301 | writer(" case \(language.name)") 302 | } 303 | } 304 | 305 | writer("}") // End of enum 306 | } 307 | 308 | // Add extension for LanguageType protocol 309 | do { 310 | writer("") 311 | writer("/**") 312 | writer(" Locale conforms to LanguageType.") 313 | writer("*/") 314 | writer("extension MNYLocale: LanguageType {") 315 | writer("") 316 | writer(" /// - returns: the lanauge identifier as a String.") 317 | writer(" public var languageIdentifier: String {") 318 | writer(" switch self {") 319 | 320 | for language in info.languages.sorted() { 321 | if language.countryIds.count > 1 { 322 | writer(" case .\(language.name)(_):") 323 | writer(" return \"\(language.id)\"") 324 | } 325 | else { 326 | writer(" case .\(language.name):") 327 | writer(" return \"\(language.id)\"") 328 | } 329 | } 330 | 331 | writer(" }") // End of switch 332 | writer(" }") // End of var 333 | writer("}") // End of extension 334 | } 335 | 336 | // Add extension for CountryType protocol 337 | do { 338 | writer("") 339 | writer("/**") 340 | writer(" Locale conforms to CountryType.") 341 | writer("*/") 342 | writer("extension MNYLocale: CountryType {") 343 | writer("") 344 | writer(" /// - returns: the country identifier as a String.") 345 | writer(" public var countryIdentifier: String {") 346 | writer(" switch self {") 347 | 348 | let caseNames = info.languagesWithLessThanTwoCountries.map { $0.caseNameValue } 349 | let joinedCaseNames = caseNames.joined(separator: ", ") 350 | writer(" case \(joinedCaseNames):") 351 | writer(" return \"\"") 352 | 353 | for language in info.languagesWithMoreThanOneCountry { 354 | writer(" case .\(language.name)(let country):") 355 | writer(" return country.countryIdentifier") 356 | } 357 | 358 | writer(" }") // End of switch 359 | writer(" }") // End of var 360 | writer("}") // End of extension 361 | } 362 | 363 | // Add extension for LocaleType protocol 364 | do { 365 | writer("") 366 | writer("extension MNYLocale: LocaleType {") 367 | writer(" // Uses default implementation") 368 | writer("}") // End of extension 369 | } 370 | } 371 | 372 | func createLocaleTypes(withWriter writer: Writer) { 373 | 374 | // Create the (Language)SpeakingCountry enum types 375 | createLanguageSpeakingCountries(withWriter: writer) 376 | 377 | // Create the Locale enum 378 | createLocale(withWriter: writer) 379 | } 380 | 381 | // MARK: - Unit Tests 382 | 383 | func createUnitTestImports(withWriter writer: Writer) { 384 | writer("import XCTest") 385 | writer("@testable import Money") 386 | } 387 | 388 | func createXCTestCaseNamed(withWriter writer: Writer, className: String, content: Generator) { 389 | writer("") 390 | writer("class \(className)AutogeneratedTests: XCTestCase {") 391 | content(writer) 392 | writer("}") 393 | } 394 | 395 | func createTestForCountryIdentifierFromCountryCaseName(withWriter writer: Writer, country: Country) { 396 | writer("") 397 | writer(" func test__country_identifier_for_\(country.name)() {") 398 | writer(" country = .\(country.name)") 399 | writer(" XCTAssertEqual(country.countryIdentifier, \"\(country.id)\")") 400 | writer(" }") 401 | } 402 | 403 | func createUnitTestsForLanguageSpeakingCountry(withWriter writer: Writer, language: Language) { 404 | let name = language.languageSpeakingCountryEnumName 405 | createXCTestCaseNamed(withWriter: writer, className: name) { line in 406 | writer("") 407 | writer(" var country: \(name)!") 408 | for country in language.countryIds.flatMap({ info.countriesById[$0] }) { 409 | createTestForCountryIdentifierFromCountryCaseName(withWriter: writer, country: country) 410 | } 411 | } 412 | } 413 | 414 | func createUnitTestsForLanguageSpeakingCountries(withWriter writer: Writer) { 415 | writer("") 416 | writer("// MARK: - Country Types Tests") 417 | for language in info.languagesWithMoreThanOneCountry { 418 | createUnitTestsForLanguageSpeakingCountry(withWriter: writer, language: language) 419 | } 420 | } 421 | 422 | func createTestForLanguageIdentifier(withWriter writer: Writer, language: Language, country: Country? = nil) { 423 | writer("") 424 | if let country = country { 425 | writer(" func test__language_identifier_for_\(language.name)_\(country.name)() {") 426 | writer(" locale = .\(language.name)(\(country.caseNameValue))") 427 | writer(" XCTAssertEqual(locale.languageIdentifier, \"\(language.id)\")") 428 | writer(" XCTAssertEqual(locale.localeIdentifier, \"\(language.id)_\(country.id)\")") 429 | writer(" }") 430 | } 431 | else { 432 | writer(" func test__language_identifier_for_\(language.name)() {") 433 | writer(" locale = .\(language.name)") 434 | writer(" XCTAssertEqual(locale.languageIdentifier, \"\(language.id)\")") 435 | writer(" XCTAssertEqual(locale.localeIdentifier, \"\(language.id)\")") 436 | writer(" }") 437 | } 438 | } 439 | 440 | func createUnitTestsForLocaleWithLanguage(withWriter writer: Writer, language: Language) { 441 | if language.countryIds.count < 2 { 442 | createTestForLanguageIdentifier(withWriter: writer, language: language) 443 | } 444 | else { 445 | for country in language.countryIds.flatMap({ info.countriesById[$0] }) { 446 | createTestForLanguageIdentifier(withWriter: writer, language: language, country: country) 447 | } 448 | } 449 | } 450 | 451 | func createUnitTestsForLocale(withWriter writer: Writer) { 452 | writer("") 453 | writer("// MARK: - Locale Tests") 454 | 455 | for language in info.languagesWithMoreThanOneCountry { 456 | 457 | createXCTestCaseNamed(withWriter: writer, className: "MNYLocale\(language.name)Language") { writer in 458 | writer("") 459 | writer(" var locale: MNYLocale!") 460 | 461 | createUnitTestsForLocaleWithLanguage(withWriter: writer, language: language) 462 | } 463 | } 464 | 465 | createXCTestCaseNamed(withWriter: writer, className: "MNYLocale") { line in 466 | writer("") 467 | writer(" var locale: MNYLocale!") 468 | 469 | for language in info.languagesWithLessThanTwoCountries { 470 | createUnitTestsForLocaleWithLanguage(withWriter: writer, language: language) 471 | } 472 | } 473 | } 474 | 475 | // MARK: - Generators 476 | 477 | func generateSourceCode(to outputPath: String) { 478 | 479 | guard let outputStream = OutputStream(toFileAtPath: outputPath, append: false) else { 480 | fatalError("Unable to create output stream at path: \(outputPath)") 481 | } 482 | 483 | defer { 484 | outputStream.close() 485 | } 486 | 487 | let write: Writer = { str in 488 | guard let data = str.data(using: String.Encoding.utf8) else { 489 | fatalError("Unable to encode str: \(str)") 490 | } 491 | let _ = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) } 492 | } 493 | 494 | let writeLine: Writer = { write("\($0)\n") } 495 | 496 | outputStream.open() 497 | createFrontMatter(withWriter: writeLine) 498 | 499 | 500 | createExtension(for: "Currency", withWriter: writeLine, content: createCurrencyTypes) 501 | write("\n") 502 | createMoneyTypes(withWriter: writeLine) 503 | write("\n") 504 | createLocaleTypes(withWriter: writeLine) 505 | } 506 | 507 | func generateUnitTests(to outputPath: String) { 508 | 509 | guard let outputStream = OutputStream(toFileAtPath: outputPath, append: false) else { 510 | fatalError("Unable to create output stream at path: \(outputPath)") 511 | } 512 | 513 | defer { 514 | outputStream.close() 515 | } 516 | 517 | let write: Writer = { str in 518 | guard let data = str.data(using: String.Encoding.utf8) else { 519 | fatalError("Unable to encode str: \(str)") 520 | } 521 | let _ = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) } 522 | } 523 | 524 | let writeLine: Writer = { write("\($0)\n") } 525 | 526 | outputStream.open() 527 | 528 | createFrontMatter(withWriter: writeLine) 529 | 530 | createUnitTestImports(withWriter: writeLine) 531 | 532 | createUnitTestsForLanguageSpeakingCountries(withWriter: writeLine) 533 | 534 | createUnitTestsForLocale(withWriter: writeLine) 535 | } 536 | 537 | // MARK: - Main() 538 | let process = Process() 539 | 540 | let pathToSourceCodeFile = "\(process.currentDirectoryPath)/Sources/Autogenerated.swift" 541 | generateSourceCode(to: pathToSourceCodeFile) 542 | 543 | let pathToUnitTestsFile = "\(process.currentDirectoryPath)/Tests/AutogeneratedTests.swift" 544 | generateUnitTests(to: pathToUnitTestsFile) 545 | 546 | -------------------------------------------------------------------------------- /Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MONEY_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Supporting Files/Money.h: -------------------------------------------------------------------------------- 1 | // 2 | // Money 3 | // 4 | // Copyright © 2016 Money. All rights reserved. 5 | // 6 | 7 | @import Foundation; 8 | 9 | //! Project version number for Money. 10 | FOUNDATION_EXPORT double MoneyVersionNumber; 11 | 12 | //! Project version string for Money. 13 | FOUNDATION_EXPORT const unsigned char MoneyVersionString[]; 14 | 15 | 16 | -------------------------------------------------------------------------------- /Supporting Files/Money.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Money 3 | // 4 | // Copyright © 2016 Money. All rights reserved. 5 | // 6 | 7 | #include "Version.xcconfig" 8 | 9 | // Metadata 10 | INFOPLIST_FILE_framework = $(SRCROOT)/Supporting Files/Info.plist 11 | INFOPLIST_FILE_xctest = $(SRCROOT)/Tests/Info.plist 12 | INFOPLIST_FILE = $(INFOPLIST_FILE_$(WRAPPER_EXTENSION)) 13 | 14 | PRODUCT_BUNDLE_IDENTIFIER_framework = me.danthorpe.Money 15 | PRODUCT_BUNDLE_IDENTIFIER_xctest = me.danthorpe.MoneyTests 16 | PRODUCT_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER_$(WRAPPER_EXTENSION)) 17 | 18 | PRODUCT_NAME_framework = Money 19 | PRODUCT_NAME_xctest = MoneyTests 20 | PRODUCT_NAME = $(PRODUCT_NAME_$(WRAPPER_EXTENSION)) 21 | 22 | APPLICATION_EXTENSION_API_ONLY_framework = YES 23 | APPLICATION_EXTENSION_API_ONLY_xctest = NO 24 | APPLICATION_EXTENSION_API_ONLY = $(APPLICATION_EXTENSION_API_ONLY_$(WRAPPER_EXTENSION)) 25 | 26 | SWIFT_VERSION = 3.0 27 | 28 | // Build Settings 29 | SUPPORTED_PLATFORMS = macosx iphoneos appletvos watchos appletvsimulator iphonesimulator watchsimulator 30 | RESOURCES_TARGETED_DEVICE_FAMILY = 1,2,3,4 31 | DYLIB_INSTALL_NAME_BASE = @rpath 32 | 33 | // Code Signing 34 | CODE_SIGN_IDENTITY = - 35 | 36 | // Deployment 37 | DEFINES_MODULE = YES 38 | 39 | MACOSX_DEPLOYMENT_TARGET = 10.11 40 | IPHONEOS_DEPLOYMENT_TARGET = 8.0 41 | TVOS_DEPLOYMENT_TARGET = 9.2 42 | WATCHOS_DEPLOYMENT_TARGET = 2.2 43 | 44 | LD_RUNPATH_SEARCH_PATHS_framework = @executable_path/../Frameworks @loader_path/Frameworks 45 | LD_RUNPATH_SEARCH_PATHS_xctest = @loader_path/Frameworks @executable_path/Frameworks @loader_path/../Frameworks @executable_path/../Frameworks 46 | LD_RUNPATH_SEARCH_PATHS = $(LD_RUNPATH_SEARCH_PATHS_$(WRAPPER_EXTENSION)) 47 | 48 | #include "Warnings.xcconfig" 49 | #include "Carthage.xcconfig" 50 | 51 | -------------------------------------------------------------------------------- /Supporting Files/Version.xcconfig: -------------------------------------------------------------------------------- 1 | MONEY_VERSION = 2.0.1 2 | -------------------------------------------------------------------------------- /Supporting Files/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Money 3 | // 4 | // Copyright © 2016 Money. All rights reserved. 5 | // 6 | 7 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES 8 | CLANG_WARN_BOOL_CONVERSION = YES 9 | CLANG_WARN_CONSTANT_CONVERSION = YES 10 | CLANG_WARN_EMPTY_BODY = YES 11 | CLANG_WARN_ENUM_CONVERSION = YES 12 | CLANG_WARN_INFINITE_RECURSION = YES 13 | CLANG_WARN_INT_CONVERSION = YES 14 | CLANG_WARN_SUSPICIOUS_MOVE = YES 15 | CLANG_WARN_UNREACHABLE_CODE = YES 16 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 17 | ENABLE_STRICT_OBJC_MSGSEND = YES 18 | ENABLE_TESTABILITY = YES 19 | GCC_NO_COMMON_BLOCKS = YES 20 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES 21 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR 22 | GCC_WARN_UNDECLARED_SELECTOR = YES 23 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE 24 | GCC_WARN_UNUSED_FUNCTION = YES 25 | GCC_WARN_UNUSED_VARIABLE = YES 26 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR 27 | CLANG_ANALYZER_NONNULL = YES 28 | -------------------------------------------------------------------------------- /Tests/ApplePayTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | #if os(iOS) 28 | 29 | import XCTest 30 | import PassKit 31 | @testable import Money 32 | 33 | class ApplePayTests: XCTestCase { 34 | 35 | var item: PaymentSummaryItem! 36 | var items: Set> = [] 37 | 38 | override func setUp() { 39 | super.setUp() 40 | item = PaymentSummaryItem(label: "iPad Pro, 32GB with WiFi", cost: 679, type: .final) 41 | items.insert(item) 42 | } 43 | } 44 | 45 | class PaymentSummaryItemTests: ApplePayTests { 46 | 47 | func test__init__sets_money() { 48 | XCTAssertEqual(item.cost, 679) 49 | } 50 | 51 | func test__init__sets_label() { 52 | XCTAssertEqual(item.label, "iPad Pro, 32GB with WiFi") 53 | } 54 | 55 | func test__init__sets_type() { 56 | XCTAssertEqual(item.type, PaymentSummaryItemType.final) 57 | } 58 | 59 | func test__set_new_money__sets_money() { 60 | item = item.set(cost: 799) 61 | XCTAssertEqual(item.cost, 799) 62 | XCTAssertEqual(item.label, "iPad Pro, 32GB with WiFi") 63 | XCTAssertEqual(item.type, PaymentSummaryItemType.final) 64 | } 65 | 66 | func test__set_new_label__sets_label() { 67 | item = item.set(label: "iPad Pro, 128GB with WiFi") 68 | XCTAssertEqual(item.cost, 679) 69 | XCTAssertEqual(item.label, "iPad Pro, 128GB with WiFi") 70 | XCTAssertEqual(item.type, PaymentSummaryItemType.final) 71 | } 72 | 73 | func test__set_new_type__sets_type() { 74 | item = item.set(type: .pending) 75 | XCTAssertEqual(item.cost, 0) 76 | XCTAssertEqual(item.label, "iPad Pro, 32GB with WiFi") 77 | XCTAssertEqual(item.type, PaymentSummaryItemType.pending) 78 | } 79 | 80 | func test__equality() { 81 | XCTAssertEqual(item, PaymentSummaryItem(label: "iPad Pro, 32GB with WiFi", cost: 679, type: .final)) 82 | XCTAssertNotEqual(item, PaymentSummaryItem(label: "iPad Pro, 128GB with WiFi", cost: 799, type: .final)) 83 | } 84 | } 85 | 86 | class PaymentSummaryItemCodingTests: ApplePayTests { 87 | 88 | func archiveEncoded() -> Data { 89 | return NSKeyedArchiver.archivedData(withRootObject: item.encoded) 90 | } 91 | 92 | func unarchive(_ archive: Data) -> PaymentSummaryItem? { 93 | return PaymentSummaryItem.decode(NSKeyedUnarchiver.unarchiveObject(with: archive) as AnyObject?) 94 | } 95 | 96 | func test__encode_decode() { 97 | XCTAssertEqual(unarchive(archiveEncoded()), item) 98 | } 99 | } 100 | 101 | class PKPaymentSummaryItemTypeTests: ApplePayTests { 102 | 103 | func test__init__final() { 104 | let type = PKPaymentSummaryItemType(paymentSummaryItemType: .final) 105 | XCTAssertEqual(type, PKPaymentSummaryItemType.final) 106 | } 107 | 108 | func test__init__pending() { 109 | let type = PKPaymentSummaryItemType(paymentSummaryItemType: .pending) 110 | XCTAssertEqual(type, PKPaymentSummaryItemType.pending) 111 | } 112 | } 113 | 114 | class PKPaymentSummaryItemTests: ApplePayTests { 115 | 116 | func test__init__with_item() { 117 | let summaryItem = PKPaymentSummaryItem(paymentSummaryItem: item) 118 | XCTAssertEqual(summaryItem.amount, NSDecimalNumber(value: 679)) 119 | XCTAssertEqual(summaryItem.label, "iPad Pro, 32GB with WiFi") 120 | XCTAssertEqual(summaryItem.type, PKPaymentSummaryItemType.final) 121 | } 122 | } 123 | 124 | class PKPaymentRequestTests: ApplePayTests { 125 | 126 | func test__init__with_items() { 127 | items.insert(PaymentSummaryItem(label: "iPad Pro, 128GB with WiFi", cost: 799, type: .final)) 128 | items.insert(PaymentSummaryItem(label: "iPad Pro, 128GB with WiFi + Cellular", cost: 899, type: .final)) 129 | let request = PKPaymentRequest(items: Array(items), sellerName: "Acme. Inc") 130 | 131 | XCTAssertEqual(request.currencyCode, GBP.Currency.code) 132 | XCTAssertEqual(request.paymentSummaryItems.count, 4) 133 | XCTAssertEqual(request.paymentSummaryItems.last!.label, "Acme. Inc") 134 | XCTAssertEqual(request.paymentSummaryItems.last!.amount, items.map { $0.cost }.reduce(0, +).amount) 135 | } 136 | } 137 | 138 | #endif 139 | 140 | -------------------------------------------------------------------------------- /Tests/BitcoinTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | 31 | class BitcoinCurrencyTests: XCTestCase { 32 | 33 | func test__xbt_currency_code() { 34 | XCTAssertEqual(Currency.XBT.code, "XBT") 35 | } 36 | 37 | func test__btc_currency_code() { 38 | XCTAssertEqual(Currency.BTC.code, "BTC") 39 | } 40 | 41 | func test__btc_currency_symbol() { 42 | XCTAssertEqual(Currency.BTC.symbol, "Ƀ") 43 | } 44 | 45 | func test__btc_currency_scale() { 46 | XCTAssertEqual(Currency.BTC.scale, 8) 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Tests/DecimalNumberTypeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | class DecimalNumberTypeTests: XCTestCase { 31 | 32 | var decimal: PlainDecimal! 33 | var money: Money! 34 | 35 | func test__init_with_int() { 36 | let value: Int = 10 37 | decimal = PlainDecimal(value) 38 | money = Money(value) 39 | XCTAssertEqual(decimal, 10) 40 | XCTAssertEqual(money, 10) 41 | } 42 | 43 | func test__init_with_uint8() { 44 | let value: UInt8 = 10 45 | decimal = PlainDecimal(value) 46 | money = Money(value) 47 | XCTAssertEqual(decimal, 10) 48 | XCTAssertEqual(money, 10) 49 | } 50 | 51 | func test__init_with_int8() { 52 | let value: Int8 = 10 53 | decimal = PlainDecimal(value) 54 | money = Money(value) 55 | XCTAssertEqual(decimal, 10) 56 | XCTAssertEqual(money, 10) 57 | } 58 | 59 | func test__init_with_uint16() { 60 | let value: UInt16 = 10 61 | decimal = PlainDecimal(value) 62 | money = Money(value) 63 | XCTAssertEqual(decimal, 10) 64 | XCTAssertEqual(money, 10) 65 | } 66 | 67 | func test__init_with_int16() { 68 | let value: Int16 = 10 69 | decimal = PlainDecimal(value) 70 | money = Money(value) 71 | XCTAssertEqual(decimal, 10) 72 | XCTAssertEqual(money, 10) 73 | } 74 | 75 | func test__init_with_uint32() { 76 | let value: UInt32 = 10 77 | decimal = PlainDecimal(value) 78 | money = Money(value) 79 | XCTAssertEqual(decimal, 10) 80 | XCTAssertEqual(money, 10) 81 | } 82 | 83 | func test__init_with_int32() { 84 | let value: Int32 = 10 85 | decimal = PlainDecimal(value) 86 | money = Money(value) 87 | XCTAssertEqual(decimal, 10) 88 | XCTAssertEqual(money, 10) 89 | } 90 | 91 | func test__init_with_uint64() { 92 | let value: UInt64 = 10 93 | decimal = PlainDecimal(value) 94 | money = Money(value) 95 | XCTAssertEqual(decimal, 10) 96 | XCTAssertEqual(money, 10) 97 | } 98 | 99 | func test__init_with_int64() { 100 | let value: Int64 = 10 101 | decimal = PlainDecimal(value) 102 | money = Money(value) 103 | XCTAssertEqual(decimal, 10) 104 | XCTAssertEqual(money, 10) 105 | } 106 | 107 | func test__init_with_float() { 108 | let value: Float = 9.0 109 | decimal = PlainDecimal(value) 110 | money = Money(value) 111 | XCTAssertEqual(decimal, 9.0) 112 | XCTAssertEqual(money, 9.00) 113 | } 114 | 115 | func test__init_with_double() { 116 | let value: Double = 9.999 117 | decimal = PlainDecimal(value) 118 | money = Money(value) 119 | XCTAssertEqual(decimal, 9.999) 120 | XCTAssertEqual(money, 10.00) 121 | } 122 | 123 | func testPerformanceInitInt() { 124 | measure { 125 | for value in 1...10_000 { 126 | self.money = Money(value) 127 | } 128 | } 129 | } 130 | 131 | func testPerformanceInitDouble() { 132 | let value: Double = 9.99 133 | measure { 134 | for _ in 1...10_000 { 135 | self.money = Money(value) 136 | } 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /Tests/DecimalTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | import ValueCoding 29 | @testable import Money 30 | 31 | class PlainDecimalTestCase: XCTestCase { 32 | var decimal: PlainDecimal! 33 | 34 | override func tearDown() { 35 | decimal = nil 36 | super.tearDown() 37 | } 38 | } 39 | 40 | class DecimalAccessorTests: PlainDecimalTestCase { 41 | 42 | func test__decimal_integer_value() { 43 | decimal = 10.00 44 | XCTAssertEqual(decimal.integerValue, 10) 45 | } 46 | 47 | func test__decimal_float_value() { 48 | decimal = 10.00 49 | XCTAssertEqual(decimal.floatValue, 10.0) 50 | } 51 | } 52 | 53 | class DecimalDescriptionTests: PlainDecimalTestCase { 54 | 55 | func test__decimal_decription1() { 56 | decimal = 10.00 57 | XCTAssertEqual(decimal.description, "10") 58 | } 59 | 60 | func test__decimal_decription2() { 61 | decimal = 10.01 62 | XCTAssertEqual(decimal.description, "10.01") 63 | } 64 | } 65 | 66 | class DecimalReciprocalTests: PlainDecimalTestCase { 67 | 68 | func test__reciprocal() { 69 | decimal = 10 70 | XCTAssertEqual(decimal.reciprocal, 0.1) 71 | } 72 | 73 | func test__reciprocal_unity() { 74 | decimal = 1 75 | XCTAssertEqual(decimal.reciprocal, 1) 76 | } 77 | } 78 | 79 | class DecimalNumberConversionTests: PlainDecimalTestCase { 80 | var money: GBP! 81 | 82 | override func setUp() { 83 | super.setUp() 84 | decimal = 10 85 | money = 20 86 | } 87 | 88 | override func tearDown() { 89 | money = nil 90 | super.tearDown() 91 | } 92 | 93 | func test__multiply() { 94 | let result = decimal * money 95 | XCTAssertEqual(result.description, "£200.00") 96 | XCTAssertEqual(money * decimal, 200) 97 | } 98 | 99 | func test__divide() { 100 | let result = decimal / money 101 | XCTAssertEqual(result.description, "£0.50") 102 | XCTAssertEqual(money / decimal, 2) 103 | } 104 | } 105 | 106 | class DecimalValueCodingTests: PlainDecimalTestCase { 107 | 108 | func archiveEncodedDecimal() -> Data { 109 | return NSKeyedArchiver.archivedData(withRootObject: decimal.encoded) 110 | } 111 | 112 | func unarchive(_ archive: Data) -> PlainDecimal? { 113 | return PlainDecimal.decode(NSKeyedUnarchiver.unarchiveObject(with: archive) as AnyObject?) 114 | } 115 | 116 | func test__decimal_encodes() { 117 | decimal = 10 118 | XCTAssertEqual(unarchive(archiveEncodedDecimal()), decimal) 119 | } 120 | } 121 | 122 | 123 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/LocaleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | class LocaleTests: XCTestCase { 31 | 32 | var en_US: NSLocale! 33 | var es_ES: NSLocale! 34 | 35 | override func setUp() { 36 | super.setUp() 37 | en_US = NSLocale(localeIdentifier: MNYLocale.English(.UnitedStates).localeIdentifier) 38 | es_ES = NSLocale(localeIdentifier: MNYLocale.Spanish(.Spain).localeIdentifier) 39 | } 40 | 41 | override func tearDown() { 42 | en_US = nil 43 | es_ES = nil 44 | super.tearDown() 45 | } 46 | 47 | func test__currency_code() { 48 | XCTAssertEqual(en_US.mny_currencyCode, "USD") 49 | XCTAssertEqual(es_ES.mny_currencyCode, "EUR") 50 | } 51 | 52 | func test__currency_symbol() { 53 | XCTAssertEqual(en_US.mny_currencySymbol, "$") 54 | XCTAssertEqual(es_ES.mny_currencySymbol, "€") 55 | } 56 | 57 | func test__currency_currencyGroupingSeparator() { 58 | XCTAssertEqual(en_US.mny_currencyGroupingSeparator, ",") 59 | XCTAssertEqual(es_ES.mny_currencyGroupingSeparator, ".") 60 | } 61 | 62 | func test__currency_currencyDecimalSeparator() { 63 | XCTAssertEqual(en_US.mny_currencyDecimalSeparator, ".") 64 | XCTAssertEqual(es_ES.mny_currencyDecimalSeparator, ",") 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /Tests/MoneyTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | class MoneyTestCase: XCTestCase { 31 | 32 | var money: Money! 33 | var gbp: GBP! 34 | var usd: USD! 35 | var cad: CAD! 36 | var aud: AUD! 37 | var eur: EUR! 38 | var jpy: JPY! 39 | var btc: BTC! 40 | 41 | override func tearDown() { 42 | super.tearDown() 43 | money = nil 44 | gbp = nil 45 | usd = nil 46 | cad = nil 47 | eur = nil 48 | jpy = nil 49 | btc = nil 50 | } 51 | } 52 | 53 | class MoneyInitializerTests: MoneyTestCase { 54 | 55 | func test__money_initialize_with__nothing() { 56 | money = Money() 57 | XCTAssertEqual(money, 0) 58 | } 59 | 60 | func test__money_initialize_with__one_int() { 61 | money = Money(integerLiteral: 1) 62 | XCTAssertEqual(money, 1) 63 | } 64 | 65 | func test__money_is_negative() { 66 | money = -10 67 | XCTAssertTrue(money.isNegative) 68 | } 69 | 70 | func test__money_can_be_negated() { 71 | money = 10 72 | XCTAssertEqual(money.negative, -10) 73 | } 74 | 75 | func test__money_amount() { 76 | money = 10 77 | XCTAssertEqual(money.amount, NSDecimalNumber(value: 10)) 78 | } 79 | } 80 | 81 | class MoneyEqualityTests: XCTestCase { 82 | var aMoney: USD! 83 | var bMoney: USD! 84 | 85 | func test__money_equals_money() { 86 | aMoney = 6.66 87 | bMoney = 6.66 88 | XCTAssertEqual(aMoney, bMoney) 89 | } 90 | 91 | func test__money_does_not_equal_money() { 92 | aMoney = 6.66 93 | bMoney = 5.66 94 | XCTAssertNotEqual(aMoney, bMoney) 95 | } 96 | } 97 | 98 | class MoneyComparableTests: XCTestCase { 99 | 100 | func test__money_sorts() { 101 | let monies: [Money] = [ 0, 12, 4.50, 9.99, 99, 9.99, 2.49, 16.69] 102 | let sorted = monies.sorted() 103 | XCTAssertEqual(sorted, [0, 2.49, 4.50, 9.99, 9.99, 12, 16.69, 99]) 104 | } 105 | } 106 | 107 | class MoneySignedNumberTests: XCTestCase { 108 | 109 | func test__money_negates() { 110 | let money: Money = 16.49 111 | let modified = -money 112 | XCTAssertEqual(modified, Money(floatLiteral: -16.49)) 113 | } 114 | 115 | func test__money_subtracts() { 116 | let a: Money = 16.49 117 | let b: Money = 6.49 118 | let result = a - b 119 | XCTAssertEqual(result, Money(integerLiteral: 10)) 120 | } 121 | } 122 | 123 | class MoneySubtractionTests: XCTestCase { 124 | 125 | let money: JPY = 12_345.67 126 | let other: JPY = 10_000 127 | 128 | func test__subtraction_int_1() { 129 | XCTAssertEqual(money - 10_000, 2_345.67) 130 | } 131 | 132 | func test__subtraction_int_2() { 133 | XCTAssertEqual(10_000 - money, -2_345.67) 134 | } 135 | 136 | func test__subtraction_float_1() { 137 | XCTAssertEqual(money - 2_345.67, 10_000) 138 | } 139 | 140 | func test__subtraction_float_2() { 141 | XCTAssertEqual(2_345.67 - money, -10_000) 142 | } 143 | } 144 | 145 | class MoneyAddingTests: XCTestCase { 146 | 147 | let money: INR = 335_577.99 148 | let other: INR = 446_688.00 149 | 150 | func test_addition() { 151 | XCTAssertEqual(money + other, 782_265.99) 152 | XCTAssertEqual(other + money, 782_265.99) 153 | } 154 | 155 | func test__addition_int_interal() { 156 | XCTAssertEqual(money + 10_000, 345_577.99) 157 | XCTAssertEqual(10_000 + money, 345_577.99) 158 | } 159 | 160 | func test__addition_float_interal() { 161 | XCTAssertEqual(money + 2_345.67, 337_923.66) 162 | XCTAssertEqual(2_345.67 + money, 337_923.66) 163 | } 164 | } 165 | 166 | class MoneyRemainderTests: XCTestCase { 167 | var dividend: EUR! 168 | var divisor: EUR! 169 | 170 | func test__remainer_all_positive() { 171 | dividend = 37.50 172 | divisor = 5 173 | XCTAssertEqual(dividend % divisor, 2.50) 174 | } 175 | 176 | func test__remainer_all_negative() { 177 | dividend = -37.50 178 | divisor = -5 179 | XCTAssertEqual(dividend % divisor, -2.50) 180 | } 181 | 182 | func test__remainer_negative_divisor() { 183 | dividend = 37.50 184 | divisor = -5 185 | XCTAssertEqual(dividend % divisor, 2.50) 186 | } 187 | 188 | func test__remainer_negative_dividend() { 189 | dividend = -37.50 190 | divisor = 5 191 | XCTAssertEqual(dividend % divisor, -2.50) 192 | } 193 | } 194 | 195 | class MoneyMultiplicationTests: XCTestCase { 196 | let money: CNY = 9.99 197 | 198 | func test__multiplication_int_0() { 199 | XCTAssertEqual(0 * money, 0) 200 | } 201 | 202 | func test__multiplication_int_1() { 203 | XCTAssertEqual(money * 1, money) 204 | } 205 | 206 | func test__multiplication_int_2() { 207 | XCTAssertEqual(money * 417, 4_165.83) 208 | } 209 | 210 | func test__multiplication_float_0() { 211 | XCTAssertEqual(0.0 * money, 0) 212 | } 213 | 214 | func test__multiplication_float_1() { 215 | XCTAssertEqual(money * 1.0, money) 216 | } 217 | 218 | func test__multiplication_float_2() { 219 | XCTAssertEqual(money * M_PI, 31.37) 220 | } 221 | } 222 | 223 | class MoneyDivisionTests: XCTestCase { 224 | let money: EUR = 9.99 225 | 226 | /* 227 | func test__division_int_0() { 228 | result = money / 0 229 | // This does throw an exception - but how can I 230 | // write a test to verify that it does? 231 | } 232 | */ 233 | 234 | func test__division_int_1() { 235 | XCTAssertEqual(money / 1, money) 236 | } 237 | 238 | func test__multiplication_int_2() { 239 | XCTAssertEqual(money / 4, 2.50) 240 | } 241 | 242 | func test__division_float_1() { 243 | XCTAssertEqual(money / 1.0, money) 244 | } 245 | 246 | func test__division_float_2() { 247 | XCTAssertEqual(money / 4.0, 2.50) 248 | } 249 | 250 | func test__division_float_3() { 251 | XCTAssertEqual(money / 0.5, 19.98) 252 | } 253 | 254 | func test__division_float_4() { 255 | XCTAssertEqual(money / M_PI, 3.18) 256 | } 257 | } 258 | 259 | class MoneyConversionTests: XCTestCase { 260 | let input: GBP = 100 261 | 262 | func test__convert_with_rate_to_other() { 263 | let output: EUR = input.convert(withRate: 1.2) 264 | XCTAssertEqual(output, 120) 265 | } 266 | } 267 | 268 | class MoneyDescriptionTests: MoneyTestCase { 269 | 270 | override func setUp() { 271 | super.setUp() 272 | money = 3.99 273 | gbp = 100 274 | usd = 99 275 | cad = 102.01 276 | aud = 99.999 277 | eur = 249.499 278 | jpy = 32_000 279 | btc = 0.002_007 280 | } 281 | 282 | func test__money_description() { 283 | XCTAssertEqual(money.description.endIndex, money.description.range(of: "3.99")?.upperBound) 284 | } 285 | 286 | func test__gbp_description() { 287 | XCTAssertEqual(gbp.currencyCode, "GBP") 288 | XCTAssertEqual(gbp.currencySymbol, "£") 289 | XCTAssertEqual(gbp.description, "£100.00") 290 | } 291 | 292 | func test__usd_formatted_with_style() { 293 | XCTAssertEqual(usd.currencyCode, "USD") 294 | let formatted = usd.formatted(withStyle: .currency, forLocale: .English(.UnitedStates)) 295 | XCTAssertEqual(formatted, "US$99.00") 296 | } 297 | 298 | func test__btc_formatted_with_style() { 299 | XCTAssertEqual(btc.currencyCode, "BTC") 300 | let formatted = btc.formatted(withStyle: .currency, forLocale: .English(.UnitedStates)) 301 | XCTAssertEqual(formatted, "Ƀ0.002007") 302 | } 303 | 304 | func test__btc_formatted_with_style_for_locale() { 305 | XCTAssertEqual(btc.currencyCode, "BTC") 306 | let formatted = btc.formatted(withStyle: .currency, forLocale: .Spanish(.Mexico)) 307 | XCTAssertEqual(formatted, "Ƀ0.002007") 308 | } 309 | 310 | func test__cad_description() { 311 | XCTAssertEqual(cad.currencyCode, "CAD") 312 | XCTAssertEqual(cad.description, "CA$102.01") 313 | } 314 | 315 | func test__aud_description() { 316 | XCTAssertEqual(aud.currencyCode, "AUD") 317 | XCTAssertEqual(aud.description, "A$100.00") 318 | } 319 | 320 | func test__eur_description() { 321 | XCTAssertEqual(eur.currencyCode, "EUR") 322 | XCTAssertEqual(eur.description, "€249.50") 323 | } 324 | 325 | func test__jpy_description() { 326 | XCTAssertEqual(jpy.currencyCode, "JPY") 327 | XCTAssertEqual(JPY.Currency.scale, 0) 328 | if NSLocale.current.identifier == "en_US" { } 329 | else { 330 | XCTAssertEqual(jpy.description, "JP¥32,000") 331 | } 332 | } 333 | 334 | func test__jpy_formatted_with_style_for_locale() { 335 | let formatted = jpy.formatted(withStyle: .currency, forLocale: .German(.Germany)) 336 | XCTAssertEqual(formatted, "32.000 JP¥") 337 | } 338 | } 339 | 340 | class MoneyFormattingTests: MoneyTestCase { 341 | 342 | override func setUp() { 343 | super.setUp() 344 | gbp = 100 345 | usd = 99 346 | cad = 102.01 347 | aud = 99.999 348 | eur = 249.499 349 | jpy = 32_000 350 | } 351 | 352 | // Tests assume a en_GB test environment 353 | func test__locale_identifier_equals_current_locale() { 354 | let gb = NSLocale.current.identifier == MNYLocale.English(.UnitedKingdom).localeIdentifier 355 | let us = NSLocale.current.identifier == MNYLocale.English(.UnitedStates).localeIdentifier 356 | XCTAssertTrue(gb || us) 357 | } 358 | 359 | func test__formatted_for_Spanish_Spain() { 360 | let result = gbp.formatted(withStyle: .currency, forLocale: .Spanish(.Spain)) 361 | XCTAssertEqual(result, "100,00 £") 362 | } 363 | 364 | func test__formatted_for_English_UnitedKingdom() { 365 | let result = gbp.formatted(withStyle: .currency, forLocale: .English(.UnitedKingdom)) 366 | XCTAssertEqual(result, "£100.00") 367 | } 368 | } 369 | 370 | class MoneyValueCodingTests: XCTestCase { 371 | 372 | var money: Money! 373 | 374 | func archiveEncodedMoney() -> Data { 375 | return NSKeyedArchiver.archivedData(withRootObject: money.encoded) 376 | } 377 | 378 | func unarchive(_ archive: Data) -> Money? { 379 | return Money.decode(NSKeyedUnarchiver.unarchiveObject(with: archive) as AnyObject?) 380 | } 381 | 382 | func test__money_encodes() { 383 | money = 10 384 | XCTAssertEqual(unarchive(archiveEncodedMoney()), money) 385 | } 386 | } 387 | 388 | class MoneyMinorUnitTests: XCTestCase { 389 | 390 | func test__money_with_USD_minor_amount_equality() { 391 | XCTAssertEqual(USD(minorUnits: 3250), 32.50) 392 | } 393 | 394 | func test__money_with_JPY_minor_amount_equality() { 395 | XCTAssertEqual(JPY(minorUnits: 2170), 2170) 396 | } 397 | 398 | func test__money_with_BTC_minor_amount_equality() { 399 | XCTAssertEqual(BTC(minorUnits: 3000), 0.00003) 400 | } 401 | 402 | func test__money_access_minor_units() { 403 | XCTAssertEqual(JPY(integerLiteral: 1).minorUnits, 1) 404 | XCTAssertEqual(USD(integerLiteral: 1).minorUnits, 100) 405 | XCTAssertEqual(BTC(integerLiteral: 1).minorUnits, 1_0000_0000) 406 | } 407 | } 408 | 409 | class CustomCurrencyWithoutSymbol: CustomCurrencyType { 410 | static let code: String = "DAN" 411 | static let scale: Int = 3 412 | static let symbol: String? = nil 413 | } 414 | 415 | -------------------------------------------------------------------------------- /Tests/NSDecimalNumberTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | class NSDecimalNumberTests: XCTestCase { 31 | 32 | var a: NSDecimalNumber! 33 | var b: NSDecimalNumber! 34 | var behaviors: NSDecimalNumberBehaviors! 35 | 36 | override func setUp() { 37 | super.setUp() 38 | a = 10 39 | b = 20 40 | behaviors = DecimalNumberBehavior.Plain.decimalNumberBehaviors 41 | } 42 | 43 | override func tearDown() { 44 | a = nil 45 | b = nil 46 | behaviors = nil 47 | super.tearDown() 48 | } 49 | 50 | func test__zero_is_not_equal_to_one() { 51 | XCTAssertNotEqual(NSDecimalNumber.zero, NSDecimalNumber.one) 52 | } 53 | 54 | func test__zero_is_less_than_one() { 55 | XCTAssertTrue(NSDecimalNumber.zero < NSDecimalNumber.one) 56 | } 57 | 58 | func test__zero_is_greater_than_negative_one() { 59 | XCTAssertTrue(NSDecimalNumber.zero > NSDecimalNumber.one.negate(withBehavior: behaviors)) 60 | } 61 | 62 | func test__negative_one_is_negative() { 63 | XCTAssertTrue(NSDecimalNumber.one.negate(withBehavior: behaviors).isNegative) 64 | } 65 | 66 | func test__zero_is_not_negative() { 67 | XCTAssertFalse(NSDecimalNumber.zero.isNegative) 68 | XCTAssertFalse(NSDecimalNumber.one.isNegative) 69 | } 70 | 71 | func test__addition() { 72 | let result = a.adding(b, withBehavior: behaviors) 73 | XCTAssertEqual(result, 30) 74 | } 75 | 76 | func test__subtraction() { 77 | let result = a.subtracting(b, withBehavior: behaviors) 78 | XCTAssertEqual(result, -10) 79 | } 80 | 81 | func test__multiplication() { 82 | let result = a.multiplying(by: b, withBehavior: behaviors) 83 | XCTAssertEqual(result, 200) 84 | } 85 | 86 | func test__division() { 87 | let result = a.dividing(by: b, withBehavior: behaviors) 88 | XCTAssertEqual(result, 0.5) 89 | } 90 | 91 | func test__remainder() { 92 | let result = a.remainder(b, withBehavior: behaviors) 93 | XCTAssertEqual(result, 10) 94 | } 95 | 96 | func test__remainder_swift_documentation_examples() { 97 | // https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID63 98 | 99 | a = 9; b = 4 100 | XCTAssertEqual(a.remainder(b, withBehavior: behaviors), 1) 101 | 102 | a = -9; b = 4 103 | XCTAssertEqual(a.remainder(b, withBehavior: behaviors), -1) 104 | 105 | a = 9; b = -4 106 | XCTAssertEqual(a.remainder(b, withBehavior: behaviors), 1) 107 | 108 | a = 8; b = 2.5 109 | XCTAssertEqual(a.remainder(b, withBehavior: behaviors), 0.5) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Tests/NSDecimalTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Money, https://github.com/danthorpe/Money 3 | // Created by Dan Thorpe, @danthorpe 4 | // 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015 Daniel Thorpe 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import XCTest 28 | @testable import Money 29 | 30 | class DecimalTests: XCTestCase { 31 | 32 | var decimalNumberA: NSDecimalNumber! 33 | var decimalNumberB: NSDecimalNumber! 34 | var behaviors: NSDecimalNumberBehaviors! 35 | var a: Decimal! 36 | var b: Decimal! 37 | 38 | override func setUp() { 39 | super.setUp() 40 | decimalNumberA = 10 41 | decimalNumberB = 20 42 | behaviors = DecimalNumberBehavior.Plain.decimalNumberBehaviors 43 | a = decimalNumberA.decimalValue 44 | b = decimalNumberB.decimalValue 45 | } 46 | 47 | override func tearDown() { 48 | decimalNumberA = nil 49 | decimalNumberB = nil 50 | behaviors = nil 51 | a = nil 52 | b = nil 53 | super.tearDown() 54 | } 55 | 56 | func test__zero() { 57 | XCTAssertEqual(Decimal.zero, NSDecimalNumber.zero.decimalValue) 58 | } 59 | 60 | func test__zero_is_not_equal_to_one() { 61 | XCTAssertNotEqual(Decimal.zero, Decimal.one) 62 | } 63 | 64 | func test__zero_is_less_than_one() { 65 | XCTAssertTrue(Decimal.zero < Decimal.one) 66 | } 67 | 68 | func test__zero_is_greater_than_negative_one() { 69 | XCTAssertTrue(Decimal.zero > Decimal.one.negate(withRoundingMode: behaviors.roundingMode())) 70 | } 71 | 72 | func test__negative_one_is_negative() { 73 | XCTAssertTrue(Decimal.one.negate(withRoundingMode: behaviors.roundingMode()).isNegative) 74 | } 75 | 76 | func test__zero_is_not_negative() { 77 | XCTAssertFalse(Decimal.zero.isNegative) 78 | XCTAssertFalse(Decimal.one.isNegative) 79 | } 80 | 81 | func test__addition() { 82 | let result = a.adding(b, withRoundingMode: behaviors.roundingMode()) 83 | let _result = decimalNumberA.adding(decimalNumberB, withBehavior: behaviors) 84 | XCTAssertEqual(result, _result.decimalValue) 85 | XCTAssertEqual(_result, 30) 86 | } 87 | 88 | func test__subtraction() { 89 | let result = a.subtracting(b, withRoundingMode: behaviors.roundingMode()) 90 | let _result = decimalNumberA.subtracting(decimalNumberB, withBehavior: behaviors) 91 | XCTAssertEqual(result, _result.decimalValue) 92 | XCTAssertEqual(_result, -10) 93 | } 94 | 95 | func test__multiplication() { 96 | let result = a.multiplying(by: b, withRoundingMode: behaviors.roundingMode()) 97 | let _result = decimalNumberA.multiplying(by: decimalNumberB, withBehavior: behaviors) 98 | XCTAssertEqual(result, _result.decimalValue) 99 | XCTAssertEqual(_result, 200) 100 | } 101 | 102 | func test__division() { 103 | let result = a.dividing(by: b, withRoundingMode: behaviors.roundingMode()) 104 | let _result = decimalNumberA.dividing(by: decimalNumberB, withBehavior: behaviors) 105 | XCTAssertEqual(result, _result.decimalValue) 106 | XCTAssertEqual(_result, 0.5) 107 | } 108 | 109 | func test__remainder() { 110 | let result = a.remainder(b, withRoundingMode: behaviors.roundingMode()) 111 | let _result = decimalNumberA.remainder(decimalNumberB, withBehavior: behaviors) 112 | XCTAssertEqual(result, _result.decimalValue) 113 | XCTAssertEqual(_result, 10) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danthorpe/Money/a8675ca149aa08c5978c2dab7f1aae346be5e486/header.png --------------------------------------------------------------------------------