Skip to content

Commit cc06f1d

Browse files
committedDec 4, 2024
Add yearly savings calculations
1 parent b690eb7 commit cc06f1d

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed
 

‎RELEASE_NOTES.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This version adds more utilities.
1212
### ✨ Features
1313

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

1617

1718

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//
2+
// Product+YearlySaving.swift
3+
// StoreKitPlus
4+
//
5+
// Created by Daniel Saidi on 2024-12-04.
6+
// Copyright © 2024 Daniel Saidi. All rights reserved.
7+
//
8+
9+
import StoreKit
10+
11+
public extension Product {
12+
13+
/// Get a yearly savings in percentages when comparing a
14+
/// yearly product with a monthy one.
15+
///
16+
/// - Returns: A raw 0-1 percentage.
17+
static func yearlySavingsPercentage(
18+
forYearlyProduct yearly: Product,
19+
comparedToMonthlyProduct monthly: Product
20+
) -> Decimal? {
21+
yearlySavingsPercentage(
22+
forYearlyPrice: yearly.price,
23+
comparedToMonthlyPrice: monthly.price
24+
)
25+
}
26+
27+
/// Get a yearly savings in percentages when comparing a
28+
/// yearly price with a monthy one.
29+
///
30+
/// - Returns: A raw 0-1 percentage.
31+
static func yearlySavingsPercentage(
32+
forYearlyPrice yearly: Decimal,
33+
comparedToMonthlyPrice monthly: Decimal
34+
) -> Decimal? {
35+
guard yearly > 0 else { return nil }
36+
guard monthly > 0 else { return nil }
37+
let percentage = 1 - (yearly / (12 * monthly))
38+
return percentage
39+
}
40+
41+
/// Get a yearly savings in percentages when comparing a
42+
/// yearly product with a monthy product variant.
43+
///
44+
/// - Returns: A 0-100 (not 0-1) display percentage.
45+
static func yearlySavingsDisplayPercentage(
46+
forYearlyProduct yearly: Product,
47+
comparedToMonthlyProduct monthly: Product
48+
) -> Int? {
49+
yearlySavingsDisplayPercentage(
50+
forYearlyPrice: yearly.price,
51+
comparedToMonthlyPrice: monthly.price
52+
)
53+
}
54+
55+
/// Get a yearly savings in percentages when comparing a
56+
/// yearly product with a monthy product variant.
57+
///
58+
/// - Returns: A 0-100 (not 0-1) display percentage.
59+
static func yearlySavingsDisplayPercentage(
60+
forYearlyPrice yearly: Decimal,
61+
comparedToMonthlyPrice monthly: Decimal
62+
) -> Int? {
63+
yearlySavingsPercentage(
64+
forYearlyPrice: yearly,
65+
comparedToMonthlyPrice: monthly
66+
)?.asDisplayPercentageValue()
67+
}
68+
}
69+
70+
private extension Decimal {
71+
72+
func asDisplayPercentageValue() -> Int {
73+
let number = self as NSNumber
74+
let result = 100 * Double(truncating: number)
75+
return Int(result.rounded())
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import XCTest
2+
import StoreKit
3+
import StoreKitPlus
4+
5+
final class Product_YearlySavingTests: XCTestCase {
6+
7+
func testCalculatingYearlySavingsPercentageRequiresPositivePrices() throws {
8+
let result1 = Product.yearlySavingsPercentage(
9+
forYearlyPrice: -1,
10+
comparedToMonthlyPrice: 10
11+
)
12+
let result2 = Product.yearlySavingsPercentage(
13+
forYearlyPrice: 100,
14+
comparedToMonthlyPrice: -1
15+
)
16+
XCTAssertNil(result1)
17+
XCTAssertNil(result2)
18+
}
19+
20+
func testCanCalculateYearlySavingsPercentage() throws {
21+
let percentage = Product.yearlySavingsPercentage(
22+
forYearlyPrice: 60,
23+
comparedToMonthlyPrice: 10 // 120
24+
)
25+
XCTAssertEqual(percentage, 0.5)
26+
}
27+
28+
func testCanCalculateYearlySavingsDisplayPercentage() throws {
29+
let percentage = Product.yearlySavingsDisplayPercentage(
30+
forYearlyPrice: 100,
31+
comparedToMonthlyPrice: 10 // 120
32+
)
33+
XCTAssertEqual(percentage, 17)
34+
}
35+
}
36+
37+
private struct TestTransaction: ValidatableTransaction {
38+
39+
let expirationDate: Date?
40+
let revocationDate: Date?
41+
}

0 commit comments

Comments
 (0)