SoulverCore is a math engine that calculates day-to-day mathematical expressions. It comes out of the popular notepad calculator app Soulver, which has been available for the Mac since 2005.
The design goals of SoulverCore are:
Sensible defaults for most use-cases (common tasks work out-of-the-box)
A high level of customizability (non-common tasks are possible, like adding phrase functions & new units)
Exceptional performance
Additionally, SoulverCore has been designed to have no 3rd party dependencies & work across all Apple platforms.
Most calculations are evaluated by SoulverCore in less than half a millisecond ⚡️! So, while SoulverCore classes are thread-safe, it’s so fast that there is typically no need to perform single calculations off the main thread of your application.
Requirements
Xcode 14+
Swift 5.7+
SoulverCore is distributed as a binary framework (.xcframework) and includes builds for macOS (universal), iOS/iPadOS, and Mac Catalyst.
The minimum system requirements are macOS 10.15 Catalina & iOS 13 (the first releases to support Swift Concurrency features)
Installation using the Swift Package Manager (SPM)
Xcode 12 and later lets you integrate SoulverCore into your project using the Swift Package Manager.
Drag SoulverCore.xcframework into the Frameworks, Libraries, and Embedded Content section of the General settings for your Mac or iOS target.
Getting Started
To calculate the result of a single expression, use a Calculator object:
import SoulverCore
let calculator = Calculator(customization: .standard)
let result = calculator.calculate("123 + 456")
print("The answer is \(result.stringValue)") // prints 579
SoulverCore can perform all sorts of calculations, including unit conversions, date & calendar math, rate calculations, percentage phrase functions, time zone conversions, and much more. It also cleverly ignores “meaningless” words:
calculator.calculate("$10 for lunch + 15% tip") // $11.50
calculator.calculate("65 kg in pounds") // 143.3 lb
calculator.calculate("40 as % of 90") // 44.44%
calculator.calculate("$150 is 25% on what") // $120
calculator.calculate("$25/hour * 14 hours of work") // $350.00
calculator.calculate("January 30 2020 + 3 months 2 weeks 5 days") // May 19, 2020
calculator.calculate("9:35am in New York to Japan") // 10:35 pm
calculator.calculate("$25k over 10 years at 7.5%") // $51,525.79 (compound interest)
Variables
Use a VariableList to set values for words or phrases in your expression:
SoulverCore respects the decimal separator and thousands separator of the system locale. Alternatively, you can convert the standard EngineCustomization to another locale:
let europeanLocale = Locale(identifier: "en_DE")
let localizedCustomization = EngineCustomization.standard.convertTo(locale: europeanLocale)
let calculator = Calculator(customization: localizedCustomization)
/// In Germany a comma is used as a decimal separator
calculator.calculate("1,2 + 3,4") // 4,6
Output Formatting
Use a FormattingPreferences to customize the way your result is formatted (how many decimal places to include, should the thousands separator be inserted, etc).
The .standardEngineCustomization uses hard-coded rates for 190 real-world & crypto-currencies. You can (and should) provide SoulverCore with up-to-date rates by setting the currencyRateProvider on your EngineCustomization to an object that conforms to CurrencyRateProvider.
SoulverCore includes one CurrencyRateProvider you can use to fetch rates from the European Central Bank for 33 popular fiat currencies.
/// This is a currency rate provider that fetches 33 popular fiat currencies from the European Central Bank, no API key required
let ecbCurrencyRateProvider = ECBCurrencyRateProvider()
/// Create a customization with this rate provider
var customizationWithLiveCurrencyRates = EngineCustomization.standard
customizationWithLiveCurrencyRates.currencyRateProvider = ecbCurrencyRateProvider
/// Create a calculator that uses this customization
let calculator = Calculator(customization: customizationWithLiveCurrencyRates)
/// Update to the latest rates...
ecbCurrencyRateProvider.updateRates { success in
if success {
// The standard customization will now have access to the latest currency rates
let result = calculator.calculate("10 USD in EUR")
print(result.stringValue)
}
}
You can create your own object that conforms to CurrencyRateProvider to provide rates for the currency codes you support. The CurrencyRateProvider protocol has a single method that returns the amount of a given currency that 1.0 USD can buy:
func rateFor(request: CurrencyRateRequest) -> Decimal? {
let currencyCode = request.currencyCode // EUR, GBP, BTC, etc
/// - Return an up-to-date rate in the form of how much 1 USD can purchase of the requested currency (i.e 1 USD = x EUR?)
/// - If your rates are in terms of how much USD the requested currency can purchase (i.e 1 EUR = x USD?), remember to take the inverse by dividing 1 by your rate
return <# Currency Rate #>
}
Rates are only requested from a CurrencyRateProvider at evaluation-time, so you don’t need to recreate or Calculator with a new EngineCustomization when your currency rate data source is updated. However you must reevaluate your line or expression: the latest rates for any currencies used will be fetched from your provider, if necessary.
Custom Units
You can add custom units to an EngineCustomization object required by the initializer on Calculator
/// A good omakase EngineCustomization (the same used by Soulver.app)
var customization: EngineCustomization = .standard
/// Set an array of custom units defined in terms of an existing unit in SoulverCore
customization.customUnits = [
CustomUnit(name: "parrots", definition: 15, equivalentUnit: .centimeters),
CustomUnit(name: "python", definition: 570, equivalentUnit: .centimeters)
]
/// Create a Calculator using this customization
let calculator = Calculator(customization: customization)
/// python and parrots are now recognized as units
calculator.calculate("1 python in parrots") // 38 parrots
Custom Functions
The syntax of a function in SoulverCore is flexible. We support traditional C-style “func(x)” functions, Swift-style “calculate(withParameter: x)” functions, or even natural phrases like “calculate x”.
You can add custom functions objects to the EngineCustomization required by the initializer on Calculator. Here is an example of a custom function that takes 1 off a given number:
/// Get the default Engine Customization
var customization: EngineCustomization = .standard
/// A prototype expression is an example of what the user will type to invoke your function
/// - For example, the following function will trigger for any phrase with the form 'number before x', where x is some number
customization.customFunctions = [CustomFunction(prototypeExpression: "number before 9", handler: { parameters in
guard let parameterDecimalValue = parameters[0].decimalValue else {
return EvaluationResult.none
}
return .decimal(parameterDecimalValue - 1.0)
})]
let calculator = Calculator(customization: customization)
let result = calculator.calculate("number before 35")
print(result.stringValue) // prints '34'
Variable Declarations
A variable declaration is any phrase followed by equals and a value (i.e, “total expenses = 123”).
Variable declarations are off by default, but can be enabled on your EngineCustomization and used on Calculator and LineCollection.
/// Get the default Engine Customization
var customization: EngineCustomization = .standard
/// Add the variable declarations feature
customization.featureFlags.variableDeclarations = true
/// Use this customization with a new Calculator object
let calculator = Calculator(customization: customization)
_ = calculator.calculate("tax = 25%")
let result = calculator.calculate("tax of $45k") // $11,250.00
Localizations
In addition to English, SoulverCore is fully localized into German, Russian, French, Spanish & simplified Chinese.
The various number & date formats of these various locales are also fully supported.
Also note that non-English languages are additive, meaning that, for instance, a German user would be able to use both English & German syntaxes.
See Also
Natural language date parsing from strings
SoulverCore includes a powerful natural language date parsing engine that is much more versatile than Foundation’s DataDetector.
See NaturalLanguageDateParsing to learn about how SoulverCore can help you parse natural language dates out of strings, and can be used to add a natural language date input field to your scheduling or calendar app (similar to features found in Things and Fantastical).
Data extraction from strings
See SoulverStringParsing to learn about how SoulverCore can help you parse data out from strings in a type-safe way. It uses a natural and memorable syntax that’s much more user friendly than regex for many tasks.
Adding calculation capabilities to an NSTextView or UITextView
See the SoulverTextKit project for an example of how to integrate the SoulverCore math engine into a standard macOS or iOS text view.
Licence
You may use SoulverCore in personal or private projects. Please email us if you wish to use SoulverCore in a publicly available, or commercial project.
We have various options available depending on your user base size, including a free license (with attribution).
What is SoulverCore?
SoulverCore is a math engine that calculates day-to-day mathematical expressions. It comes out of the popular notepad calculator app Soulver, which has been available for the Mac since 2005.
The design goals of SoulverCore are:
Additionally, SoulverCore has been designed to have no 3rd party dependencies & work across all Apple platforms.
Some apps using SoulverCore
Spotlight Replacements
Notepads
Shortcut Action Providers
Performance
Most calculations are evaluated by SoulverCore in less than half a millisecond ⚡️! So, while SoulverCore classes are thread-safe, it’s so fast that there is typically no need to perform single calculations off the main thread of your application.
Requirements
Installation using the Swift Package Manager (SPM)
Xcode 12 and later lets you integrate SoulverCore into your project using the Swift Package Manager.
In Xcode, go File > Swift Packages > Add Package Dependency and paste in the URL of this repository (https://github.com/soulverteam/SoulverCore).
Manual Installation
Drag
SoulverCore.xcframework
into theFrameworks, Libraries, and Embedded Content
section of the General settings for your Mac or iOS target.Getting Started
To calculate the result of a single expression, use a
Calculator
object:SoulverCore can perform all sorts of calculations, including unit conversions, date & calendar math, rate calculations, percentage phrase functions, time zone conversions, and much more. It also cleverly ignores “meaningless” words:
Variables
Use a
VariableList
to set values for words or phrases in your expression:Locale Settings
SoulverCore respects the decimal separator and thousands separator of the system locale. Alternatively, you can convert the standard EngineCustomization to another locale:
Output Formatting
Use a
FormattingPreferences
to customize the way your result is formatted (how many decimal places to include, should the thousands separator be inserted, etc).Live Real-World & Crypto-Currency Rates
The
.standard
EngineCustomization
uses hard-coded rates for 190 real-world & crypto-currencies. You can (and should) provide SoulverCore with up-to-date rates by setting thecurrencyRateProvider
on yourEngineCustomization
to an object that conforms toCurrencyRateProvider
.SoulverCore includes one
CurrencyRateProvider
you can use to fetch rates from the European Central Bank for 33 popular fiat currencies.You can create your own object that conforms to
CurrencyRateProvider
to provide rates for the currency codes you support. TheCurrencyRateProvider
protocol has a single method that returns the amount of a given currency that 1.0 USD can buy:Rates are only requested from a
CurrencyRateProvider
at evaluation-time, so you don’t need to recreate orCalculator
with a newEngineCustomization
when your currency rate data source is updated. However you must reevaluate your line or expression: the latest rates for any currencies used will be fetched from your provider, if necessary.Custom Units
You can add custom units to an
EngineCustomization
object required by the initializer onCalculator
Custom Functions
The syntax of a function in SoulverCore is flexible. We support traditional C-style “func(x)” functions, Swift-style “calculate(withParameter: x)” functions, or even natural phrases like “calculate x”.
You can add custom functions objects to the
EngineCustomization
required by the initializer onCalculator
. Here is an example of a custom function that takes 1 off a given number:Variable Declarations
A variable declaration is any phrase followed by equals and a value (i.e, “total expenses = 123”).
Variable declarations are off by default, but can be enabled on your
EngineCustomization
and used onCalculator
andLineCollection
.Localizations
In addition to English, SoulverCore is fully localized into German, Russian, French, Spanish & simplified Chinese.
The various number & date formats of these various locales are also fully supported.
Also note that non-English languages are additive, meaning that, for instance, a German user would be able to use both English & German syntaxes.
See Also
Natural language date parsing from strings
SoulverCore includes a powerful natural language date parsing engine that is much more versatile than Foundation’s
DataDetector
.See NaturalLanguageDateParsing to learn about how SoulverCore can help you parse natural language dates out of strings, and can be used to add a natural language date input field to your scheduling or calendar app (similar to features found in Things and Fantastical).
Data extraction from strings
See SoulverStringParsing to learn about how SoulverCore can help you parse data out from strings in a type-safe way. It uses a natural and memorable syntax that’s much more user friendly than regex for many tasks.
Adding calculation capabilities to an NSTextView or UITextView
See the SoulverTextKit project for an example of how to integrate the SoulverCore math engine into a standard macOS or iOS text view.
Licence
You may use SoulverCore in personal or private projects. Please email us if you wish to use SoulverCore in a publicly available, or commercial project.
We have various options available depending on your user base size, including a free license (with attribution).