Malwarelytics for Apple
Malwarelytics for Apple protects your iOS, tvOS or macOS app from a broad range of cyber threats. Integrate mobile threat protection SDK and connect to our cloud service to get the best benefit.
Supported Features
Malwarelytics for Apple currently supports the following features:
- Jailbreak Detection
- Debugger Protection
- Reverse Engineering Tools Detection
- HTTP Proxy detection
- Repackaging Detection
- Screen Capture Detection
- VPN Detection
- Active Call Detection
- App Presence Detection
- User Screenshot Detection
- System Passcode Status
- System Biometry Status
- Predefined and Custom Events
Read Next
Installation
Configuration
Recommended Responses to Security Issues
Malwarelytics for Apple provides detection of many security issues. Moreover, it provides a huge variability in both configuring these protections and reactions to security violations. Some reactions, such as app termination, are provided by the SDK. Other reactions, such as displaying a warning screen or limiting functionality, are the responsibility of the application integrating Malwarelytics for Apple.
The page provides a summary of recommended responses to various security issues that the SDK can help you with.
Summary of the Recommended Responses
Before diving into details we summarize the recommendations here. In general, we recommend:
- Terminate the app on app repackaging, when a debugger is attached, or when reverse engineering tools are detected.
- Show active warning when a jailbroken device is detected.
- Block debuggers, and hide screen contents when screen capturing is detected.
- Inform about other potential issues in some security advisor built into the app.
Responses to Security Issues
Some general approaches to handling a security violation might be:
- Display a Warning Message: The app can display a warning message informing the user that the app doesn’t support when a certain security feature is violated. This message should explain the security risks associated with using the app on such devices in such conditions and recommend fixing the issue for banking and financial operations.
- Limited Functionality: The app can limit its functionality when a security violation is detected. For example, it might disable some sensitive features like money transfers, or access to sensitive financial data to reduce the potential for unauthorized access and fraudulent activities.
- Terminate the App: If the app detects that it is running on a device with a security violation, it can immediately terminate itself to prevent any further access. This will help minimize the exposure of sensitive financial information and protect the user from potential security risks. As a general UX improvement, it is recommended to also open a website (in a browser outside the app) with an explanation while the app is being terminated.
- Silent Monitoring on Server: The app can silently monitor what’s happening on the device by sending data to the remote server. This approach is especially powerful when connected to a complex anti-fraud system analyzing inputs from various sources. The backend system can then decide the appropriate handling of the security issue. This approach should be used for all features in parallel to other approaches.
Responses to RASP Security Issues
Malwarelytics for Apple protects from a wide range of runtime issues. Some of them are more serious than others. Here we provide a list of recommended responses to these issues by a banking or a financial app.
Summary of Responses to RASP Issues
The summary here mentions RASP protection features that have a RaspObserver
callback method. These features can notify the integrating app about an issue and the app can react to it. Some of the protection behaviors are automatically provided by the SDK.
Security feature | Recommended response to security issue |
---|---|
Jailbroken device | Terminate the app. |
Repackaged app | Terminate the app. |
Attached debugger | Block debuggers or terminate the app when a debugger attaches. |
Reverse engineering tools | Terminate the app. |
Screenshots | Display a warning. |
Screen capturing | Hide content and monitor silently. |
System passcode status | Display a warning. Optionally limit sensitive functionality. |
System biometry status | Monitor silently. Optionally display a recommendation. |
HTTP proxy | Display a warning. |
VPN | Display a warning. |
Active calls | Limit sensitive functionality during a call. |
App presence | Limit sensitive functionality when an unwanted app is installed. |
Jailbroken Devices
It is generally recommended to avoid using jailbroken devices for banking or financial apps. The integrity of jailbroken devices is compromised and security is reduced. It is far easier for attackers to bypass security measures designed to protect financial data and the integrity of financial operations within the app.
For these reasons, we generally recommend terminating the app and not allowing users to use it on a jailbroken device.
Repackaged Application
A repackaged application is a changed app and can’t be considered secure. It poses a danger to both clients and the financial institution issuing the app. Any code inside a repackaged app can be inserted, changed, or removed. Therefore security features of such apps have to be considered as compromised.
For this reason, the app should unconditionally terminate itself when it detects that it has been repackaged.
Attached Debuggers
An attached debugger poses a huge danger to the app. There’s a huge number of potential issues such as data leakage, security breaches, transaction tampering, and injection of malicious code.
For these reasons, we generally recommend either blocking debuggers from attaching or terminating the app when an attached debugger is detected.
Reverse Engineering Tools
Reverse engineering tools substantially affect the security and integrity of iOS applications. These tools can be used to jailbreak the device, hook into live processes, and modify code execution in real-time. It can be used to bypass security checks, authentication mechanisms, and other protective measures.
For these reasons, the app should unconditionally terminate itself when it detects any reverse engineering tool.
Screenshots
Screenshots taken by users can contain sensitive personal and financial information. Once a screenshot is captured, the users should take care to handle it with caution to avoid accidental leakage and exposure of the data it may contain. Unfortunately, this is often not the case.
For this reason, the app should warn users about the dangers of leaking sensitive information when it detects that a screenshot has been taken.
Screen Capturing
A financial or banking app handles a lot of sensitive data. Screen capturing can record everything happening on the screen over a period of time, not just a static image. This means all displayed sensitive information, including account numbers, balances, transaction history, and personal data, can be captured in detail. The captured recording can be accidentally or maliciously shared, leading to a significant breach of confidentiality.
The recommended approach is to hide screen contents. This way the obtained recording will not contain sensitive data. Screen capturing should be monitored silently, with no action unless additional suspicious signals appear.
System Passcode Status
Usage of system passcode is an essential security feature of all iOS devices. Not having a system passcode poses significant security risks, especially if sensitive applications like banking apps are installed. The stored personal and financial information is vulnerable to unauthorized access. Moreover, when a malicious individual gets physical access to the device, the possession authentication factor is effectively eliminated on such devices, and the risk of financial fraud greatly increases. Lastly, a lack of system passcode conflicts with several security standards and regulations, which can lead to compliance issues.
The recommended response is to display a warning and optionally limit the app’s sensitive functionality.
System Biometry Status
Usage of biometrics reduces the risk of unauthorized access to the user’s device and a banking app. PINs and passwords are frequently reused by the users. Also, there is a range of attacks for obtaining the user’s password or PIN such as phishing attacks, social engineering, and shoulder surfing.
The recommended response is to monitor the issue silently. Optionally the app can attempt to display a recommendation to the user.
Active HTTP Proxy
Active HTTP proxy can exploit issues in client-server communication when not done correctly and can lead to data interception.
The recommended response to the detection of an active HTTP proxy is to display a warning to the user.
Active VPN
Active VPN can indicate a possible fraud attempt and might be a problem from the point of regulatory compliance. While VPNs are generally used to secure internet traffic, not all VPN services are trustworthy. Some might intercept, monitor, or manipulate traffic routed through them.
The recommended response to the detection of an active VPN is to display a warning to the user.
Active Calls
An active call during the active usage of a banking app poses a risk of social engineering attacks. These attacks are very frequent and create huge financial losses.
For this reason, it’s recommended to block any sensitive functionality during the period of the call. Or even automatically reject all operations and transactions initiated during an active call.
App Presence
Remote desktop apps are a concern for all banking apps due to the potential risk they pose in financial fraud. These apps enable remote access to the device, allowing unauthorized individuals to view the screen remotely. Furthermore, some remote desktop apps may include audio sharing, which could potentially allow eavesdropping through the device’s microphone. Although it is not possible to remotely control an iOS device, attackers frequently use these apps to commit financial fraud.
The recommended response is to limit functionality while an unwanted app is present on the device. All functionality involving sensitive operations and transactions should be blocked to prevent any fraudsters from seeing any sensitive data.
Read Next
RASP Features Overview
Jailbreak Detection
– | - |
| .noAction
| indicates that jailbreak will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that jailbreak will be automatically detected and the delegates will be notified via the jailbreakDetected()
method. |
| .exit(
exitUrl: String?)
| indicates that the jailbreak will be automatically detected and the app will be terminated when the jailbreak is automatically detected. |
Jailbreak detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Usage
After service creation, the jailbreak detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual jailbreak detection check.
Observing Detection
Jailbreak detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func jailbreakDetected() {
// handle jailbreak detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
Jailbreak detection check can be triggered manually in AppProtectionRasp
by getting the isJailbroken
property value. A simple Bool
answer is given.
let isJailbroken = appProtection.rasp.isJailbroken
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
Debugger Detection
– | |
| .noAction
| indicates that debuggers will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that debuggers will be automatically detected and the delegates will be notified via the debuggerDetected()
method. |
| .block
| indicates that debuggers will be blocked from attaching to the application process. |
| .exit(
exitUrl: String?)
| indicates that debuggers will be automatically detected and the app will be terminated when a debugger is automatically detected. |
Debugger detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Usage
After service creation, the debugger detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual debugger detection check.
Observing Detection
Debugger detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func debuggerDetected() {
// handle debugger detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
Debugger detection check can be triggered manually in AppProtectionRasp
by getting the isDebuggerConnected
property value. A simple Bool
answer is given.
let isDebuggerConnected = appProtection.rasp.isDebuggerConnected
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
Reverse Engineering Tools Detection
– | - |
| .noAction
| indicates that reverse engineering tools will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that reverse engineering tools will be automatically detected and the delegates will be notified via the reverseEngineeringToolsDetected()
method. |
| .exit(
exitUrl: String?)
| indicates that the reverse engineering tools will be automatically detected and the app will be terminated when the reverse engineering tools are automatically detected. |
Reverse engineering tools detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Usage
After service creation, the reverse engineering tools detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual reverse engineering tools detection check.
Observing Detection
Reverse engineering tools detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func reverseEngineeringToolsDetected() {
// handle reverse engineering tools detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
Reverse engineering tools detection check can be triggered manually in AppProtectionRasp
by getting the isReverseEngineeringToolsPresent
property value. A simple Bool
answer is given.
let isReverseEngineeringToolsPresent = appProtection.rasp.isReverseEngineeringToolsPresent
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
HTTP Proxy Detection
– | |
| .noAction
| indicates that the HTTP proxy will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that the HTTP proxy will be automatically detected and the delegates will be notified via the httpProxyEnabled()
method. |
| .exit(
exitUrl: String?)
| indicates that the HTTP proxy will be automatically detected and the app will be terminated when the HTTP proxy is automatically detected. |
HTTP proxy detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Usage
After service creation, the HTTP proxy detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual HTTP proxy detection check.
Observing Detection
HTTP proxy detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func httpProxyEnabled() {
// handle HTTP proxy detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
HTTP proxy detection check can be triggered manually in AppProtectionRasp
by getting the isHttpProxyEnabled
property value. A simple Bool
answer is given.
let isHttpProxyEnabled = appProtection.rasp.isHttpProxyEnabled
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
Repackaging Detection
– | – |
| .noAction(
trustedCerts: [TrustedCertificate])
| indicates that repackaging will not be automatically detected. A manual check is still possible. |
| .notify(
trustedCerts: [TrustedCertificate])
| indicates that repackaging will be automatically detected and the delegates will be notified via the repackageDetected()
method. |
| .exit(
trustedCerts: [TrustedCertificate],
exitUrl: String?)
| indicates that the repackaging will be automatically detected and the app will be terminated when the repackaging is automatically detected. |
Repackaging detection defaults to .noAction([])
.
List of available parameters for some config values:
Parameter | Description |
---|---|
trustedCerts: [TrustedCertificate] |
defines trusted certificates for ad-hoc or enterprise distribution. AppStore signing certificates are trusted by default. |
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Certificate Configuration Details
To properly configure the repackaging detection, you need to get the Base64 encoded string of your signing certificate:
- Open the
Keychain Access
application. - Find a certificate that will be used to sign your application, for example, “Apple Development: Jan Tester (c)”.
- Right-click on the item and click “Export…”.
- Export the certificate in the
.cer
format. - Open up the terminal and
cd
into the folder with your exported certificate. - Encode the certificate in Base64 with
cat your_exported.cer | base64
. - Copy the output of the command and use it as a parameter for the repackage detection configuration:
// Prepare the RASP feature configuration
let raspConfig = AppProtectionRaspConfig(
// ...
repackage: .exit([AppProtectionTrustedCert(withBase64EncodedString: "BASE_64_ENCODED_CERT")!], "https://myurl.com/repackage-explained")
// ...
)
Tip: To hide the string in your binary, use the init
constructor for AppProtectionTrustedCert
with Data
or [UInt8]
arguments.
Usage
After service creation, the repackaging detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual repackaging detection check.
Observing Detection
Repackaging detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func repackageDetected() {
// handle repackaging detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
The repackaging detection check can be triggered manually in AppProtectionRasp
by getting the isRepackaged
property value. A simple Bool
answer is given.
let isRepackaged = appProtection.rasp.isRepackaged
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
Screen Capture Detection
– | – |
| .noAction
| indicates that screen capture will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that screen capture will be automatically detected and the delegates will be notified via the screenCapturedChanged(Bool)
method. |
| .hide(
overlay: Overlay)
| indicates that the app will hide its content when the screen capture is detected. The delegates will be notified via the screenCapturedChanged(Bool)
method. |
| .exit(
exitUrl: String?)
| indicates that the screen capture will be automatically detected and the app will be terminated when the screen capture is automatically detected. |
Screen capture detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
overlay: Overlay |
defines the overlay that will be used to hide the contents of the app. Defaults to .default . |
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Overlay Configuration
Available value of Overlay
:
Value | Description |
---|---|
.default |
defines the default behavior that covers the screen with a solid color and application icon. |
.color( color: UIColor) |
defines that the screen will be covered with a solid color. |
.image( image: UIImage) |
defines that the screen will be covered with an image. |
Usage
After service creation, the screen capture detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual screen capture detection check.
Observing Detection
The screen capture detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func screenCapturedChanged(isCaptured: Bool) {
// handle screen capture detection (casting to a different device)
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
The screen capture detection check can be triggered manually in AppProtectionRasp
by getting the isScreenCaptured
property value. A simple Bool
answer is given.
let isScreenCaptured = appProtection.rasp.isScreenCaptured
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
VPN Detection
– | - |
| .noAction
| indicates that the VPN will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that the VPN will be automatically detected and the delegates will be notified via the vpnChanged(Bool)
method. |
| .exit(
exitUrl: String?)
| indicates that the VPN will be automatically detected and the app will be terminated when the VPN is automatically detected. |
VPN detection defaults to .notify
.
List of available parameters for some config values:
Parameter | Description |
---|---|
exitUrl: String? |
defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil . |
Usage
After service creation, the VPN detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual VPN detection check.
Observing Detection
VPN detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func vpnChanged(active: Bool) {
// handle VPN detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
VPN detection check can be triggered manually in AppProtectionRasp
by getting the isVpnActive
property value. A simple Bool
answer is given.
let isVpnActive = appProtection.rasp.isVpnActive
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
Active Call Detection
– | - |
| .noAction
| indicates that an active call will not be automatically detected. A manual check is still possible. |
| .notify
| indicates that an active call will be automatically detected and the delegates will be notified via the onCallChanged(Bool)
method. |
Active call detection defaults to .notify
.
Usage
After service creation, the active call detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual active call detection check.
Observing Detection
Active call detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func onCallChanged(isOnCall: Bool) {
// handle active call detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
Active call detection check can be triggered manually in AppProtectionRasp
by getting the isOnCall
property value. A simple Bool
answer is given.
let isOnCall = appProtection.rasp.isOnCall
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
App Presence Detection
| – |
| .manual(
apps: [DetectableApp])
| indicates that app presence will not be automatically detected. A manual check is still possible. |
| .notify(
apps: [DetectableApp])
| indicates that app presence will be automatically detected and the delegates will be notified via the installedAppsChanged([DetectableApp])
method. |
The app presence detection defaults to .manual([])
.
List of available parameters for some config values:
Parameter | Description |
---|---|
apps: [DetectableApp] |
defines the list of detectable apps. |
Detectable App Configuration
A detectable app is defined by several properties:
Property | Description |
---|---|
deeplinkProtocols: [String] |
specifies deep links defined for the app. |
name: String |
specifies name of the application. The name can be chosen at will and does not need to reflect the name in the AppStore or of an installed app. |
category: Category |
specifies category of the application. Currently only .remoteDesktop is available. |
tag: String? |
specifies any additional information that should be passed to the remote server. |
Usage
After service creation, the app presence detection feature can be accessed via AppProtectionRasp
. This can be used to add a delegate or to trigger a manual app presence detection check.
Observing Detection
The app presence detection can trigger a certain action. To achieve that, a delegate needs to be added.
Delegate configuration:
class RaspDelegate: AppProtectionRaspDelegate {
// other delegate code
func installedAppsChanged(installedApps: [DetectableApp]) {
// handle app presence detection
}
}
The delegate can be added in AppProtectionRasp
. When it is no longer needed, it can be removed again.
let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)
Triggering a Manual Check
The app presence detection check can be triggered manually in AppProtectionRasp
by getting the installedApps
property value. The method returns [DetectableApp]
.
let installedApps = appProtection.rasp.installedApps
More information on general RASP feature configuration and usage can be found in this overview.
Read Next
User Screenshot Detection
System Passcode Detection
System Biometry Detection
Release Notes
Current Release
Release 3.0.1-dev
- Improve VPN detection (#135)
- Improve jailbreak detection with detection of rootless palera1n (#136)
- Fix
.exit(String?)
with invalid exitUrl string (#134)
Previous Releases
Release 3.0.0
- Renamed the swift module to WultraAppProtection from AppProtection
Release 2.1.2
- Change debugger detection default to
.notify
(#113) - Updated TestFlight certificate
Release 2.1.1
- Add unwanted apps detection (#104)
Release 2.0.0
- Add detection of active call
- Add option to run in RASP-only (offline) mode
Release 1.2.2
- Add support for TestFlight builds (#90)
Release 1.2.1
- Remove
X-Signature-Origin-App
header from signature verification (#85) - Expose avUid (internal device id) (#87)
Release 1.2.0
- Add overlay when screen is captured (#77)
- Add VPN detection (#72)
Release 1.1.2
- Add test environment (#73)
- Improve CPU usage (#74)
Release 1.1.1
- Add
clientAppDeviceId
(#68) - Add config items for customer application package names and grouping (#67)
Release 1.1.0
- Add Swift Package Manager support
- Add other fixes and improvements
Release 1.0.3
- Fix issue when device id could be automaticaly renewed
- Add other fixes and improvements
Release 1.0.2
- Fix potential crash when the app was running for a long time
- Add other fixes and improvements
Release 1.0.1
- Add fixes and improvements
Release 1.0.0
- Remove singleton pattern. Use a public initializer to create an instance note that only one instance can be created.
- Remove configuration builders. Configuration is now struct with initializer.
- Add other fixes and improvements
Release 0.9.9
- Add fixes related to Core Data crashes
Release 0.9.8
- Initial beta release