Skip to content

Commit

Permalink
Add yearly savings calculations
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsaidi committed Dec 4, 2024
1 parent b690eb7 commit cc06f1d
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This version adds more utilities.
### ✨ Features

* `BasicProduct` is a new, lightweight product struct.
* `Product` has new ways of calculating yearly savings.



Expand Down
77 changes: 77 additions & 0 deletions Sources/StoreKitPlus/Products/Product+YearlySaving.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// Product+YearlySaving.swift
// StoreKitPlus
//
// Created by Daniel Saidi on 2024-12-04.
// Copyright © 2024 Daniel Saidi. All rights reserved.
//

import StoreKit

public extension Product {

/// Get a yearly savings in percentages when comparing a
/// yearly product with a monthy one.
///
/// - Returns: A raw 0-1 percentage.
static func yearlySavingsPercentage(
forYearlyProduct yearly: Product,
comparedToMonthlyProduct monthly: Product
) -> Decimal? {
yearlySavingsPercentage(
forYearlyPrice: yearly.price,
comparedToMonthlyPrice: monthly.price
)
}

/// Get a yearly savings in percentages when comparing a
/// yearly price with a monthy one.
///
/// - Returns: A raw 0-1 percentage.
static func yearlySavingsPercentage(
forYearlyPrice yearly: Decimal,
comparedToMonthlyPrice monthly: Decimal
) -> Decimal? {
guard yearly > 0 else { return nil }
guard monthly > 0 else { return nil }
let percentage = 1 - (yearly / (12 * monthly))
return percentage
}

/// Get a yearly savings in percentages when comparing a
/// yearly product with a monthy product variant.
///
/// - Returns: A 0-100 (not 0-1) display percentage.
static func yearlySavingsDisplayPercentage(
forYearlyProduct yearly: Product,
comparedToMonthlyProduct monthly: Product
) -> Int? {
yearlySavingsDisplayPercentage(
forYearlyPrice: yearly.price,
comparedToMonthlyPrice: monthly.price
)
}

/// Get a yearly savings in percentages when comparing a
/// yearly product with a monthy product variant.
///
/// - Returns: A 0-100 (not 0-1) display percentage.
static func yearlySavingsDisplayPercentage(
forYearlyPrice yearly: Decimal,
comparedToMonthlyPrice monthly: Decimal
) -> Int? {
yearlySavingsPercentage(
forYearlyPrice: yearly,
comparedToMonthlyPrice: monthly
)?.asDisplayPercentageValue()
}
}

private extension Decimal {

func asDisplayPercentageValue() -> Int {
let number = self as NSNumber
let result = 100 * Double(truncating: number)
return Int(result.rounded())
}
}
41 changes: 41 additions & 0 deletions Tests/StoreKitPlusTests/Products/Product+YearlySavingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import XCTest
import StoreKit
import StoreKitPlus

final class Product_YearlySavingTests: XCTestCase {

func testCalculatingYearlySavingsPercentageRequiresPositivePrices() throws {
let result1 = Product.yearlySavingsPercentage(
forYearlyPrice: -1,
comparedToMonthlyPrice: 10
)
let result2 = Product.yearlySavingsPercentage(
forYearlyPrice: 100,
comparedToMonthlyPrice: -1
)
XCTAssertNil(result1)
XCTAssertNil(result2)
}

func testCanCalculateYearlySavingsPercentage() throws {
let percentage = Product.yearlySavingsPercentage(
forYearlyPrice: 60,
comparedToMonthlyPrice: 10 // 120
)
XCTAssertEqual(percentage, 0.5)
}

func testCanCalculateYearlySavingsDisplayPercentage() throws {
let percentage = Product.yearlySavingsDisplayPercentage(
forYearlyPrice: 100,
comparedToMonthlyPrice: 10 // 120
)
XCTAssertEqual(percentage, 17)
}
}

private struct TestTransaction: ValidatableTransaction {

let expirationDate: Date?
let revocationDate: Date?
}

0 comments on commit cc06f1d

Please sign in to comment.