From 8fad3d3471b0098b25cfdfd14cc98de6645d9730 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 20 Feb 2024 08:55:38 +0400 Subject: [PATCH 01/13] Update the Documentation (#23) * Update the documentation * Update `README.md` * Update `CHANGELOG.md` --- CHANGELOG.md | 6 ++++++ README.md | 7 ++++++- Sources/Flare/Classes/Models/StoreProduct.swift | 2 +- .../Classes/Models/SubscriptionEligibility.swift | 8 ++++---- .../IConfigurationProvider.swift | 2 +- Sources/Flare/Flare.docc/Articles/logging.md | 2 +- .../Flare/Flare.docc/Articles/perform-purchase.md | 13 ++++++++++--- 7 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c2d4c03..8bf4e3daf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## [Unreleased] + +## Fixed +- Update the documentation + - Fixed in Pull Request[#23](https://github.com/space-code/flare/pull/23). + #### 3.x Releases - `3.0.0` Release Candidates - [`3.0.0-rc.1`](#300-rc1) diff --git a/README.md b/README.md index 63b899b10..8b229a25d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Flare is a framework written in Swift that makes it easy for you to work with in - [Installation](#installation) - [Communication](#communication) - [Contributing](#contributing) +- [Have a Question](#have-a-question) - [Author](#author) - [License](#license) @@ -37,7 +38,7 @@ Check out the [flare documentation](https://space-code.github.io/flare/documenta ## Requirements - iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 7.0+ / visionOS 1.0+ -- Xcode 14.0 +- Xcode 15.0 - Swift 5.7 ## Installation @@ -67,6 +68,10 @@ make bootstrap Please feel free to help out with this project! If you see something that could be made better or want a new feature, open up an issue or send a Pull Request! +## Have a Question? + +Contact us via [issues on GitHub](https://github.com/space-code/flare/issues). + ## Author Nikita Vasilev, nv3212@gmail.com diff --git a/Sources/Flare/Classes/Models/StoreProduct.swift b/Sources/Flare/Classes/Models/StoreProduct.swift index 3a2c4a7d8..a299804a6 100644 --- a/Sources/Flare/Classes/Models/StoreProduct.swift +++ b/Sources/Flare/Classes/Models/StoreProduct.swift @@ -15,7 +15,7 @@ public final class StoreProduct: NSObject { /// Protocol representing a Store Kit product. let product: ISKProduct - /// <#Description#> + /// The store kit product. var underlyingProduct: ISKProduct { product } // MARK: Initialization diff --git a/Sources/Flare/Classes/Models/SubscriptionEligibility.swift b/Sources/Flare/Classes/Models/SubscriptionEligibility.swift index 362884ace..f7e24a78f 100644 --- a/Sources/Flare/Classes/Models/SubscriptionEligibility.swift +++ b/Sources/Flare/Classes/Models/SubscriptionEligibility.swift @@ -5,14 +5,14 @@ import Foundation -// Enumeration defining the eligibility status for a subscription +/// Enumeration defining the eligibility status for a subscription public enum SubscriptionEligibility: Int, Sendable { - // Represents that the subscription is eligible for an offer + /// Represents that the subscription is eligible for an offer case eligible - // Represents that the subscription is not eligible for an offer + /// Represents that the subscription is not eligible for an offer case nonEligible - // Represents that there is no offer available for the subscription + /// Represents that there is no offer available for the subscription case noOffer } diff --git a/Sources/Flare/Classes/Providers/ConfigurationProvider/IConfigurationProvider.swift b/Sources/Flare/Classes/Providers/ConfigurationProvider/IConfigurationProvider.swift index e5e2649b2..88f8014cf 100644 --- a/Sources/Flare/Classes/Providers/ConfigurationProvider/IConfigurationProvider.swift +++ b/Sources/Flare/Classes/Providers/ConfigurationProvider/IConfigurationProvider.swift @@ -10,7 +10,7 @@ protocol IConfigurationProvider { /// The application username. var applicationUsername: String? { get } - /// <#Description#> + /// The cache policy for fetching data. var fetchCachePolicy: FetchCachePolicy { get } /// Configures the provider with the specified configuration settings. diff --git a/Sources/Flare/Flare.docc/Articles/logging.md b/Sources/Flare/Flare.docc/Articles/logging.md index 6df6914d4..bd324a6ad 100644 --- a/Sources/Flare/Flare.docc/Articles/logging.md +++ b/Sources/Flare/Flare.docc/Articles/logging.md @@ -1,4 +1,4 @@ -# logging +# Logging Learn how to log important events. diff --git a/Sources/Flare/Flare.docc/Articles/perform-purchase.md b/Sources/Flare/Flare.docc/Articles/perform-purchase.md index 87dd4ea88..9ab23aeef 100644 --- a/Sources/Flare/Flare.docc/Articles/perform-purchase.md +++ b/Sources/Flare/Flare.docc/Articles/perform-purchase.md @@ -29,7 +29,7 @@ Flare.shared.removeTransactionObserver() The fetch method sends a request to the App Store, which retrieves the products if they are available. The productIDs parameter takes the product ids, which should be given from the App Store. -> important: Before attempting to add a payment always check if the user can actually make payments. The Flare does it under the hood, if a user cannot make payments, you will get an ``IAPError`` with the value ``IAPError/paymentNotAllowed``. +> important: Before attempting to add a payment always check if the user can actually make payments. The Flare does it under the hood, if a user cannot make payments, you will get an ``IAPError/paymentNotAllowed``. ```swift Flare.shared.fetch(productIDs: ["product_id"]) { result in @@ -42,7 +42,7 @@ Flare.shared.fetch(productIDs: ["product_id"]) { result in } ``` -Additionally, there are versions of both fetch that provide an `async` method, allowing the use of await. +Additionally, there is an `await` version of the ``IFlare/fetch(productIDs:)`` method. ```swift do { @@ -54,6 +54,14 @@ do { > note: Products are cached by default. If caching is not possible for specific usecases, set ``Configuration/fetchCachePolicy`` to ``FetchCachePolicy/fetch``. +```swift +import Flare + +let configuration = Configuration(fetchCachePolicy: .fetch) + +Flare.configure(with: configuration) +``` + ## Purchasing Product Flare provides a few methods to perform a purchase: @@ -93,4 +101,3 @@ Flare.shared.finish(transaction: transaction, completion: nil) ``` > important: Don’t call the ``IFlare/finish(transaction:completion:)`` method before the transaction is actually complete and attempt to use some other mechanism in your app to track the transaction as unfinished. StoreKit doesn’t function that way, and doing that prevents your app from downloading Apple-hosted content and can lead to other issues. - From 9587d1c8db08754fab57eed314bab06d742149e8 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 21 Feb 2024 19:27:43 +0400 Subject: [PATCH 02/13] Implement switching to the main thread (#24) * Implement switching to the main thread * Update `CHANGELOG.md` --- CHANGELOG.md | 4 ++- .../Classes/Helpers/Async/AsyncHandler.swift | 32 ++++++++++++++++--- .../Providers/IAPProvider/IAPProvider.swift | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bf4e3daf..50aa90850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ## Fixed +- Fix switching to the main thread + - Fixed in Pull Request [#24](https://github.com/space-code/flare/pull/24). - Update the documentation - - Fixed in Pull Request[#23](https://github.com/space-code/flare/pull/23). + - Fixed in Pull Request [#23](https://github.com/space-code/flare/pull/23). #### 3.x Releases - `3.0.0` Release Candidates - [`3.0.0-rc.1`](#300-rc1) diff --git a/Sources/Flare/Classes/Helpers/Async/AsyncHandler.swift b/Sources/Flare/Classes/Helpers/Async/AsyncHandler.swift index f819b1bf5..8ffb43c1f 100644 --- a/Sources/Flare/Classes/Helpers/Async/AsyncHandler.swift +++ b/Sources/Flare/Classes/Helpers/Async/AsyncHandler.swift @@ -1,22 +1,46 @@ // // Flare -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation +// MARK: - AsyncHandler + @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.2, *) enum AsyncHandler { static func call( + strategy: Strategy = .default, completion: @escaping (Result) -> Void, asyncMethod method: @escaping () async throws -> T ) { - _ = Task { + Task { do { - try completion(.success(await method())) + let result = try await method() + await execute(strategy: strategy) { completion(.success(result)) } } catch { - completion(.failure(error)) + await execute(strategy: strategy) { completion(.failure(error)) } } } } + + // MARK: Private + + private static func execute(strategy: Strategy, block: @escaping () -> Void) async { + switch strategy { + case .runOnMain: + await MainActor.run { block() } + case .default: + block() + } + } +} + +// MARK: AsyncHandler.Strategy + +extension AsyncHandler { + enum Strategy { + case runOnMain + case `default` + } } diff --git a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift index bb386a7bf..949b80bc7 100644 --- a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift +++ b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift @@ -63,6 +63,7 @@ final class IAPProvider: IIAPProvider { func fetch(productIDs: Set, completion: @escaping Closure>) { if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { AsyncHandler.call( + strategy: .runOnMain, completion: { (result: Result<[StoreProduct], Error>) in switch result { case let .success(products): From 70b3de5f4d49d044f372a86c9dd1c7140508c464 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Thu, 22 Feb 2024 12:13:45 +0400 Subject: [PATCH 03/13] Implement asynchronous transaction completion (#25) * Implement asynchronous transaction completion * Update `CHANGELOG.md` * Implement test cases --- CHANGELOG.md | 4 ++++ Sources/Flare/Classes/Flare.swift | 4 ++++ Sources/Flare/Classes/IFlare.swift | 7 +++++++ .../Classes/Providers/IAPProvider/IAPProvider.swift | 8 ++++++++ .../Classes/Providers/IAPProvider/IIAPProvider.swift | 7 +++++++ Tests/FlareTests/UnitTests/FlareTests.swift | 11 +++++++++++ .../UnitTests/Providers/IAPProviderTests.swift | 11 +++++++++++ .../TestHelpers/Mocks/IAPProviderMock.swift | 12 ++++++++++++ .../TestHelpers/Mocks/PurchaseProviderMock.swift | 3 ++- 9 files changed, 66 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50aa90850..0924ddcaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## Added +- Implement asynchronous transaction completion + - Added in Pull Request [#25](https://github.com/space-code/flare/pull/25). + ## Fixed - Fix switching to the main thread - Fixed in Pull Request [#24](https://github.com/space-code/flare/pull/24). diff --git a/Sources/Flare/Classes/Flare.swift b/Sources/Flare/Classes/Flare.swift index 9ea74d1f4..579366f0d 100644 --- a/Sources/Flare/Classes/Flare.swift +++ b/Sources/Flare/Classes/Flare.swift @@ -136,6 +136,10 @@ extension Flare: IFlare { iapProvider.finish(transaction: transaction, completion: completion) } + public func finish(transaction: StoreTransaction) async { + await iapProvider.finish(transaction: transaction) + } + public func addTransactionObserver(fallbackHandler: Closure>?) { iapProvider.addTransactionObserver(fallbackHandler: fallbackHandler) } diff --git a/Sources/Flare/Classes/IFlare.swift b/Sources/Flare/Classes/IFlare.swift index 21856a111..277b3d79d 100644 --- a/Sources/Flare/Classes/IFlare.swift +++ b/Sources/Flare/Classes/IFlare.swift @@ -125,6 +125,13 @@ public protocol IFlare { /// - completion: If a completion closure is provided, call it after finishing the transaction. func finish(transaction: StoreTransaction, completion: (@Sendable () -> Void)?) + /// Removes a finished (i.e. failed or completed) transaction from the queue. + /// Attempting to finish a purchasing transaction will throw an exception. + /// + /// - Parameters: + /// - transaction: An object in the payment queue. + func finish(transaction: StoreTransaction) async + /// The transactions array will only be synchronized with the server while the queue has observers. /// /// - Note: This may require that the user authenticate. diff --git a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift index 949b80bc7..4ffac9238 100644 --- a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift +++ b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift @@ -166,6 +166,14 @@ final class IAPProvider: IIAPProvider { purchaseProvider.finish(transaction: transaction, completion: completion) } + func finish(transaction: StoreTransaction) async { + await withCheckedContinuation { (continuation: CheckedContinuation) in + finish(transaction: transaction) { + continuation.resume(returning: ()) + } + } + } + func addTransactionObserver(fallbackHandler: Closure>?) { purchaseProvider.addTransactionObserver(fallbackHandler: fallbackHandler) } diff --git a/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift b/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift index 3e8899936..100917471 100644 --- a/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift +++ b/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift @@ -123,6 +123,13 @@ public protocol IIAPProvider { /// - completion: If a completion closure is provided, call it after finishing the transaction. func finish(transaction: StoreTransaction, completion: (@Sendable () -> Void)?) + /// Removes a finished (i.e. failed or completed) transaction from the queue. + /// Attempting to finish a purchasing transaction will throw an exception. + /// + /// - Parameters: + /// - transaction: An object in the payment queue. + func finish(transaction: StoreTransaction) async + /// Adds transaction observer to the payment queue. /// The transactions array will only be synchronized with the server while the queue has observers. /// diff --git a/Tests/FlareTests/UnitTests/FlareTests.swift b/Tests/FlareTests/UnitTests/FlareTests.swift index 6998e78bf..945ece829 100644 --- a/Tests/FlareTests/UnitTests/FlareTests.swift +++ b/Tests/FlareTests/UnitTests/FlareTests.swift @@ -216,6 +216,17 @@ class FlareTests: XCTestCase { XCTAssertTrue(iapProviderMock.invokedFinishTransaction) } + func test_thatFlareFinishesAsyncTransaction() async { + // given + let transaction = PaymentTransaction(PaymentTransactionMock()) + + // when + await sut.finish(transaction: StoreTransaction(paymentTransaction: transaction)) + + // then + XCTAssertTrue(iapProviderMock.invokedFinishAsyncTransaction) + } + func test_thatFlareAddsTransactionObserver() { // when sut.addTransactionObserver(fallbackHandler: { _ in }) diff --git a/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift b/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift index ace7cd0e8..ac9ed1a5f 100644 --- a/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift +++ b/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift @@ -99,6 +99,17 @@ class IAPProviderTests: XCTestCase { XCTAssertTrue(purchaseProvider.invokedFinish) } + func test_thatIAPProviderFinishesAsyncTransaction() async { + // given + let transaction = PurchaseManagerTestHelper.makePaymentTransaction(state: .purchased) + + // when + await sut.finish(transaction: StoreTransaction(paymentTransaction: PaymentTransaction(transaction))) + + // then + XCTAssertTrue(purchaseProvider.invokedFinish) + } + func test_thatIAPProviderAddsTransactionObserver() { // when sut.addTransactionObserver(fallbackHandler: { _ in }) diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift index 1d05ea36c..c1f84b1f6 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift @@ -70,6 +70,18 @@ final class IAPProviderMock: IIAPProvider { invokedFinishTransactionParanetersList.append((transaction, ())) } + var invokedFinishAsyncTransaction = false + var invokedFinishAsyncTransactionCount = 0 + var invokedFinishAsyncTransactionParameters: (StoreTransaction, Void)? + var invokedFinishAsyncTransactionParanetersList = [(StoreTransaction, Void)]() + + func finish(transaction: StoreTransaction) async { + invokedFinishAsyncTransaction = true + invokedFinishAsyncTransactionCount += 1 + invokedFinishAsyncTransactionParameters = (transaction, ()) + invokedFinishAsyncTransactionParanetersList = [(transaction, ())] + } + var invokedAddTransactionObserver = false var invokedAddTransactionObserverCount = 0 var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift index 7ccf9b076..3941055b7 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift @@ -12,11 +12,12 @@ final class PurchaseProviderMock: IPurchaseProvider { var invokedFinishParameters: (transaction: StoreTransaction, Void)? var invokedFinishParametersList = [(transaction: StoreTransaction, Void)]() - func finish(transaction: StoreTransaction, completion _: (@Sendable () -> Void)?) { + func finish(transaction: StoreTransaction, completion: (@Sendable () -> Void)?) { invokedFinish = true invokedFinishCount += 1 invokedFinishParameters = (transaction, ()) invokedFinishParametersList.append((transaction, ())) + completion?() } var invokedAddTransactionObserver = false From c6eddbd9fa79321c2e0830203a5737acdf0a4240 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Fri, 23 Feb 2024 17:44:20 +0400 Subject: [PATCH 04/13] Fix Handling of Cancelling Operations (#26) * Fix handling of cancelling operations * Update `CHANGELOG.md` --- CHANGELOG.md | 2 ++ .../PurchaseProvider/PurchaseProvider.swift | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0924ddcaf..68c0d2b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. - Added in Pull Request [#25](https://github.com/space-code/flare/pull/25). ## Fixed +- Fix handling of cancelling operations + - Fixed in Pull Request [#26](https://github.com/space-code/flare/pull/26). - Fix switching to the main thread - Fixed in Pull Request [#24](https://github.com/space-code/flare/pull/24). - Update the documentation diff --git a/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift b/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift index b78680ba9..7fc89aae7 100644 --- a/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift +++ b/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift @@ -78,12 +78,20 @@ final class PurchaseProvider { Task { switch result { case let .success(result): - if let transaction = try await self.transactionListener?.handle(purchaseResult: result) { - await completion(.success(transaction)) - Logger.info(message: L10n.Purchase.purchasedProduct(sk2StoreProduct.productIdentifier)) - } else { - await completion(.failure(IAPError.unknown)) - self.log(error: IAPError.unknown, productID: sk2StoreProduct.productIdentifier) + do { + if let transaction = try await self.transactionListener?.handle(purchaseResult: result) { + await completion(.success(transaction)) + Logger.info(message: L10n.Purchase.purchasedProduct(sk2StoreProduct.productIdentifier)) + } else { + await completion(.failure(IAPError.unknown)) + self.log(error: IAPError.unknown, productID: sk2StoreProduct.productIdentifier) + } + } catch { + if let error = error as? IAPError { + await completion(.failure(error)) + } else { + await completion(.failure(IAPError.with(error: error))) + } } case let .failure(error): await completion(.failure(IAPError(error: error))) From e333aafb3ed8548dfd7469d1cfca29b80e9e3224 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Mon, 29 Apr 2024 19:01:15 +0400 Subject: [PATCH 05/13] Implement `FlareUI` (#28) * Create `FlareUI` library * Implement both `SubscriptionsView` & `ProductsView` * Implement a user interface for products * Implement fetching & purchasing storekit product via `ProductView` * Implement an appearing & disappearing animation * Fix UI elements * Implement displaying product's icon * Implement updaing view with animation * Remove the example app * Add code comments * Create a test target for `FlareUI` library * Update `SwiftLint` action * Implement `upload_test_coverage_report.yml` * Update `workflows` * Implement `build_and_test` action * Implement test cases for `ProductView` - Create the shared `FlareMock` package - Implement snapshot tests - Implement unit tests * Implement test cases for `ProductsView` * Implement store buttons - Implement the `restore` button * Implement various product styles * Update the logic for generating `swiftgen` files * Displaying an error alert when restoring purchases * Displaying a subscription duration next to the price * Implement the display of a placeholder icon * Implement tests * Update tests * Implement passing parameters to a view - Implement `onInAppPurchaseCompletion` to handle the completion of a purchase. - Implement `inAppPurchaseOptions` to pass additional parameters for a purchase. * Update the `FlareUITests` target's configuration * Update `CHANGELOG.md` * Create a test plan for snapshot testing * Update the snapshot tests * Implement the snapshot tests target * Refactor the finish transaction logic * Implement `compact` & `large` product style for iOS * Make the `large` and `compact` product styles public * Remove unnecessary test cases for specific platforms * Implement passing assemblies using `environment` * Fix displaying price for compact product style * Make `ProductView` & `ProductsView` public * Implement wrappers in `UIKit` for `ProductView` & `ProductsView` * Implement UIKit wrappers for `tvOS` and `macOS` * Add Russian localization * Implement the passing of view styles to UIKit wrappers * Implement scrolling for products * Implement sorting for fetched products * Implement the `loading` & `error` state for `SubscriptionsView` * Implement customization of subscription item styles * Update the footer view * Implement passing tint colors * Implement the creation of marketing content for the subscription view * Implement settings background for subscription view * Implement constast text * Implement the formatting of a subscription price * Implement bold button style * Implement `Privacy Policy` & `Terms of Service` buttons * Fix tests * Temp commit * Refactor `SubscriptionsWrapperView` * Update `CardButtonSubscriptionStoreControlStyle` * Implement `PoliciesButtonStyle` * Update the `CardButtonSubscriptionStoreControlView` style * Add `MARK` * Implement auto-detection of active subscriptions * Implement `SubscriptionStatusVerifierProvider` * Fix building `Flare` for macOS * Implement displaying a header for tvOS * Implement the `SubscriptionsViewController` wrapper * Implement `SubscriptionsPresenterTests` * Fix typos * Fixing the project build for Xcode 14 * Disable support for `watchOS` * Implement snapshot tests * Fix project building * Fix tests --- .github/actions/build_and_test/action.yml | 48 +++ .../upload_test_coverage_report/action.yml | 34 ++ .github/workflows/{ci.yml => flare.yml} | 96 +++--- .github/workflows/flare_ui.yml | 267 ++++++++++++++++ .github/workflows/swiftlint.yml | 26 ++ .gitignore | 4 +- .swiftformat | 3 +- .swiftlint.yml | 10 +- .../xcschemes/Flare-Package.xcscheme | 196 ++++++++++++ .../xcshareddata/xcschemes/Flare.xcscheme | 14 - .../xcshareddata/xcschemes/FlareUI.xcscheme | 105 ++++++ .../xcschemes/FlareUITests.xcscheme | 53 +++ CHANGELOG.md | 2 + Makefile | 6 +- Package.resolved | 18 ++ Package.swift | 30 ++ Package@swift-5.7.swift | 30 ++ Package@swift-5.8.swift | 30 ++ .../Flare/Classes/DI/FlareDependencies.swift | 8 +- ...nInfo.Status+ISubscriptionInfoStatus.swift | 24 ++ Sources/Flare/Classes/Flare.swift | 11 +- Sources/Flare/Classes/IFlare.swift | 9 +- .../ITransactionListener.swift | 2 + .../TransactionListener.swift | 29 +- .../TransactionListenerDelegate.swift | 13 + .../Classes/Models/ExpirationReason.swift | 37 +++ .../Internal/Protocols/IRenewalInfo.swift | 46 +++ .../Internal/Protocols/ISKProduct.swift | 3 + .../Protocols/ISubscriptionInfo.swift | 10 + .../Protocols/ISubscriptionInfoStatus.swift | 11 + .../Models/Internal/SK1StoreProduct.swift | 4 + .../Models/Internal/SK2RenewalInfo.swift | 69 ++++ .../Models/Internal/SK2StoreProduct.swift | 7 + .../Models/Internal/SK2SubscriptionInfo.swift | 32 ++ .../Internal/SK2SubscriptionInfoStatus.swift | 34 ++ .../Classes/Models/PriceIncreaseStatus.swift | 29 ++ .../Flare/Classes/Models/RenewalInfo.swift | 73 +++++ .../Flare/Classes/Models/RenewalState.swift | 35 ++ .../Flare/Classes/Models/StoreProduct.swift | 4 + .../Classes/Models/StoreTransaction.swift | 14 +- .../Classes/Models/SubscriptionInfo.swift | 39 +++ .../Models/SubscriptionInfoStatus.swift | 32 ++ .../Classes/Models/VerificationError.swift | 52 ++- .../Classes/Models/VerificationResult.swift | 11 + .../Providers/IAPProvider/IAPProvider.swift | 17 +- .../Providers/IAPProvider/IIAPProvider.swift | 9 +- .../CachingProductsProviderDecorator.swift | 18 +- .../ICachingProductsProviderDecorator.swift | 0 .../ISortingProductsProviderDecorator.swift | 8 + .../SortingProductsProviderDecorator.swift | 72 +++++ .../ProductProvider/IProductProvider.swift | 4 +- .../ProductProvider/ProductProvider.swift | 8 +- .../PurchaseProvider/IPurchaseProvider.swift | 5 +- .../PurchaseProvider/PurchaseProvider.swift | 43 ++- Sources/Flare/Makefile | 2 + swiftgen.yml => Sources/Flare/swiftgen.yml | 4 +- .../FlareMock/Fakes/StoreProduct+Fake.swift | 39 +++ .../Fakes/StoreTransaction+Fake.swift | 13 + .../Mocks/PaymentTransactionMock.swift | 48 +++ Sources/FlareMock/Mocks/ProductMock.swift | 138 ++++++++ .../Core/EnvironmentKey/AnyProductStyle.swift | 31 ++ .../AnySubscriptionControlStyle.swift | 34 ++ .../Assemblies/ProductAssemblyKey.swift | 19 ++ .../Assemblies/StoreButtonsAssemblyKey.swift | 19 ++ .../EnvironmentKey/BlurEffectStyleKey.swift | 21 ++ .../PoliciesButtonStyleKey.swift | 21 ++ .../Core/EnvironmentKey/ProductStyleKey.swift | 19 ++ .../PurchaseCompletionKey.swift | 22 ++ .../EnvironmentKey/PurchaseOptionKey.swift | 23 ++ .../Core/EnvironmentKey/StoreButtonKey.swift | 19 ++ .../StoreButtonViewFontWeightKey.swift | 19 ++ .../SubscriptionBackgroundKey.swift | 19 ++ .../SubscriptionControlStyleKey.swift | 21 ++ ...bscriptionHeaderContentBackgroundKey.swift | 19 ++ .../SubscriptionMarketingContentKey.swift | 19 ++ .../SubscriptionPickerItemBackgroundKey.swift | 19 ++ ...scriptionPrivacyPolicyDestinationKey.swift | 19 ++ .../SubscriptionPrivacyPolicyURLKey.swift | 19 ++ .../SubscriptionStoreButtonLabelKey.swift | 19 ++ ...criptionTermsOfServiceDestinationKey.swift | 19 ++ .../SubscriptionTermsOfServiceURLKey.swift | 19 ++ .../SubscriptionViewTintKey.swift | 19 ++ .../SubscriptionsWrapperViewStyleKey.swift | 21 ++ .../Core/EnvironmentKey/TintColorKey.swift | 19 ++ .../Extensions/Array+RemoveDuplicates.swift | 13 + .../Core/Extensions/String+SubSequence.swift | 13 + .../Extensions/StringProtocol+Words.swift | 16 + .../Core/Extensions/View+EraseToAnyView.swift | 12 + .../DateComponentsFormatter+Full.swift | 16 + .../Formatters/IDateComponentsFormatter.swift | 18 ++ .../Core/Helpers/Array+StoreProduct.swift | 14 + .../Classes/Core/Helpers/Color+UIColor.swift | 36 +++ .../Classes/Core/Helpers/Error+IAP.swift | 15 + .../FlareUI/Classes/Core/Helpers/Value.swift | 20 ++ .../Models/Internal/PriceDisplayFormat.swift | 13 + .../Core/Models/Internal/ProductStyle.swift | 11 + .../Classes/Core/Models/PaywallType.swift | 15 + .../Classes/Core/Models/PurchaseOptions.swift | 24 ++ .../Classes/Core/Models/SubscptionType.swift | 15 + .../SubscriptionStatusVerifierType.swift | 13 + .../Models/SubscriptionStoreButtonLabel.swift | 13 + .../Classes/Core/Models/UIConfiguration.swift | 18 ++ .../ConfigurationProvider.swift | 32 ++ .../IConfigurationProvider.swift | 12 + .../ISubscriptionStatusVerifierProvider.swift | 10 + .../SubscriptionStatusVerifierProvider.swift | 34 ++ .../ISubscriptionStatusVerifier.swift | 10 + .../SubscriptionStatusVerifier.swift | 29 ++ ...bscriptionStatusVerifierTypeResolver.swift | 10 + ...bscriptionStatusVerifierTypeResolver.swift | 22 ++ .../DI/Dependencies/FlareDependencies.swift | 29 ++ .../DI/Dependencies/IFlareDependencies.swift | 16 + .../IPresentationAssembly.swift | 18 ++ .../PresentationAssembly.swift | 58 ++++ Sources/FlareUI/Classes/FlareUI.swift | 37 +++ .../FlareUI/Classes/Generated/Colors.swift | 109 +++++++ Sources/FlareUI/Classes/Generated/Media.swift | 126 ++++++++ .../FlareUI/Classes/Generated/Strings.swift | 127 ++++++++ Sources/FlareUI/Classes/IFlareUI.swift | 8 + .../BaseHostingController.swift | 39 +++ .../Helpers/ColorRepresentation.swift | 19 ++ .../Controllers/Helpers/SUIViewWrapper.swift | 66 ++++ .../ProductViewController.swift | 79 +++++ .../ProductViewControllerViewModel.swift | 14 + .../ProductsViewController.swift | 105 ++++++ .../ProductsViewControllerViewModel.swift | 16 + .../SubscriptionsViewController.swift | 193 +++++++++++ ...SubscriptionsViewControllerViewModel.swift | 31 ++ .../ViewController/HostingController.swift | 20 ++ .../ViewController/ViewController.swift | 20 ++ .../Components/Core/Constants/Palette.swift | 62 ++++ .../Core/Constants/UIConstants.swift | 16 + .../Core/Models/StoreButtonType.swift | 15 + .../Core/Models/StoreButtonVisibility.swift | 12 + .../Components/Core/Protocols/IModel.swift | 22 ++ .../Core/Protocols/IPresenter.swift | 39 +++ .../ISubscriptionPriceViewModelFactory.swift | 13 + .../SubscriptionPriceViewModelFactory.swift | 95 ++++++ .../Helpers/SUI/View+Contrast.swift | 23 ++ .../Components/Helpers/SUI/View+Paywall.swift | 26 ++ .../Helpers/SUI/View+ProductViewStyle.swift | 12 + .../Helpers/SUI/View+PurchaseCompletion.swift | 13 + .../Helpers/SUI/View+PurchaseOption.swift | 19 ++ .../Helpers/SUI/View+StoreButton.swift | 32 ++ .../SUI/View+StoreButtonViewFontWeight.swift | 12 + .../SUI/View+SubscriptionBackground.swift | 16 + .../SUI/View+SubscriptionControlStyle.swift | 23 ++ ...+SubscriptionHeaderContentBackground.swift | 17 + .../View+SubscriptionMarketingContent.swift | 12 + ...iew+SubscriptionPickerItemBackground.swift | 16 + ...SubscriptionPrivacyPolicyDestination.swift | 12 + .../View+SubscriptionPrivacyPolicyURL.swift | 17 + .../View+SubscriptionStoreButtonLabel.swift | 16 + ...ubscriptionTermsOfServiceDestination.swift | 12 + .../View+SubscriptionTermsOfServiceURL.swift | 17 + .../SUI/View+SubscriptionViewTint.swift | 12 + .../Helpers/SUI/View+TintColor.swift | 12 + .../Helpers/UIKit/ViewController+Child.swift | 32 ++ .../Styles/BorderedButtonStyle.swift | 37 +++ .../Styles/PrimaryButtonStyle.swift | 43 +++ .../Styles/Product/CompactProductStyle.swift | 37 +++ .../ProductStyleConfiguration.swift | 62 ++++ .../Styles/Product/LargeProductStyle.swift | 41 +++ .../Protocols/IProductStyle+Compact.swift | 12 + .../Protocols/IProductStyle+Large.swift | 16 + .../Product/Protocols/IProductStyle.swift | 19 ++ ...iptionStoreControlStyleConfiguration.swift | 49 +++ .../ISubscriptionControlStyle+Bordered.swift | 14 + ...+PickerSubscriptionStoreControlStyle.swift | 14 + ...tPickerSubscriptionStoreControlStyle.swift | 16 + .../Protocols/ISubscriptionControlStyle.swift | 19 ++ .../AutomaticSubscriptionControlStyle.swift | 30 ++ ...orderedSubscriptionStoreControlStyle.swift | 43 +++ ...redSubscriptionStoreControlStyleView.swift | 96 ++++++ .../ButtonSubscriptionStoreControlStyle.swift | 43 +++ ...dButtonSubscriptionStoreControlStyle.swift | 55 ++++ ...rdButtonSubscriptionStoreControlView.swift | 98 ++++++ .../PickerSubscriptionStoreControlStyle.swift | 40 +++ ...kerSubscriptionStoreControlStyleView.swift | 138 ++++++++ ...tPickerSubscriptionStoreControlStyle.swift | 42 +++ ...kerSubscriptionStoreControlStyleView.swift | 52 +++ .../ActivityIndicatorModifier.swift | 42 +++ .../ViewModifiers/BlurEffectModifier.swift | 17 + .../ErrorAlertViewModifier.swift | 47 +++ .../ViewModifiers/LoadViewModifier.swift | 47 +++ .../ViewModifiers/PaywallViewModifier.swift | 44 +++ .../ActivityIndicatorView.swift | 69 ++++ .../BlurVisualEffectView.swift | 27 ++ .../Views/ImageView/ImageView.swift | 34 ++ .../ProductPlaceholderView.swift | 185 +++++++++++ .../Views/SafariWebView/SafariWebView.swift | 19 ++ .../Presentation/Helpers/ViewWrapper.swift | 38 +++ .../Helpers/WrapperViewModel.swift | 23 ++ .../Views/PaywallView/PaywallView.swift | 37 +++ .../PoliciesButtonAssembly.swift | 22 ++ .../PoliciesButtonView.swift | 120 +++++++ .../Styles/AnyPoliciesButtonStyle.swift | 34 ++ .../AutomaticPoliciesButtonStyle.swift | 17 + .../PoliciesButtonStyleConfiguration.swift | 23 ++ .../DefaultPoliciesButtonStyle.swift | 15 + .../DefaultPoliciesButtonStyleView.swift | 42 +++ .../Styles/IPoliciesButtonStyle.swift | 19 ++ .../TVPoliciesButtonStyle.swift | 27 ++ .../Views/PoliciesUnavailableView.swift | 66 ++++ .../Views/ProductView/ProductPresenter.swift | 66 ++++ .../ProductView/ProductPurchaseService.swift | 55 ++++ .../Views/ProductView/ProductView.swift | 29 ++ .../ProductView/ProductViewAssembly.swift | 55 ++++ .../Views/ProductView/ProductViewModel.swift | 26 ++ .../ProductView/ProductViewModelFactory.swift | 60 ++++ .../Views/ProductView/ProductViewType.swift | 11 + .../ProductView/ProductWrapperView.swift | 77 +++++ .../Strategies/ProductStrategy.swift | 43 +++ .../SubscriptionDateComponentsFactory.swift | 37 +++ .../ProductInfoView/ProductInfoView.swift | 207 ++++++++++++ .../ProductsView/ProductsPresenter.swift | 46 +++ .../Views/ProductsView/ProductsView.swift | 27 ++ .../ProductsView/ProductsViewAssembly.swift | 52 +++ .../ProductsView/ProductsViewModel.swift | 29 ++ .../ProductsView/ProductsWrapperView.swift | 72 +++++ .../Views/StoreUnavaliableView.swift | 56 ++++ .../Views/StoreButtonView/StoreButton.swift | 11 + .../StoreButtonView/StoreButtonAssembly.swift | 53 +++ .../StoreButtonPresenter.swift | 45 +++ .../StoreButtonView/StoreButtonView.swift | 50 +++ .../StoreButtonViewModel.swift | 34 ++ .../StoreButtonsAssembly.swift | 44 +++ .../AnySubscriptionsWrapperViewStyle.swift | 32 ++ ...iptionsWrapperViewStyleConfiguration.swift | 26 ++ .../ISubscriptionsWrapperViewStyle.swift | 20 ++ ...tomaticSubscriptionsWrapperViewStyle.swift | 17 + .../CompactSubscriptionWrapperView.swift | 46 +++ .../CompactSubscriptionWrapperViewStyle.swift | 17 + .../Full/FullSubscriptionsWrapperView.swift | 59 ++++ .../FullSubscriptionsWrapperViewStyle.swift | 15 + .../SubscriptionsAssembly.swift | 56 ++++ .../SubscriptionsPresenter.swift | 99 ++++++ .../SubscriptionsView/SubscriptionsView.swift | 29 ++ .../SubscriptionsViewModel.swift | 48 +++ .../SubscriptionsViewModelViewFactory.swift | 69 ++++ .../SubscriptionsWrapperView.swift | 136 ++++++++ .../SubscriptionsView/Views/LoadingView.swift | 79 +++++ .../Views/SubscriptionHeaderView.swift | 69 ++++ .../Views/SubscriptionToolbarView.swift | 137 ++++++++ .../Views/SubscriptionView.swift | 102 ++++++ Sources/FlareUI/Makefile | 2 + .../Assets.xcassets/Colors/Contents.json | 9 + .../dynamic_background.colorset/Contents.json | 68 ++++ .../Colors/gray.colorset/Contents.json | 38 +++ .../system_background.colorset/Contents.json | 68 ++++ .../Assets/Assets.xcassets/Contents.json | 6 + .../Assets/Media.xcassets/Contents.json | 6 + .../Assets/Media.xcassets/Media/Contents.json | 9 + .../Media/checkmark.imageset/Contents.json | 12 + .../checkmark.circle.fill.svg | 11 + .../Media/circle.imageset/Contents.json | 12 + .../Media/circle.imageset/circle.svg | 11 + .../Media/star.imageset/Contents.json | 12 + .../Media/star.imageset/star.svg | 11 + .../Localization/en.lproj/Localizable.strings | 20 ++ .../Localization/ru.lproj/Localizable.strings | 20 ++ Sources/FlareUI/swiftgen.yml | 24 ++ Sources/FlareUIMock/Mocks/FlareMock.swift | 302 ++++++++++++++++++ .../Mocks/ProductPresenterMock.swift | 28 ++ .../Mocks/ProductViewAssemblyMock.swift | 40 +++ .../Mocks/ProductsPresenterMock.swift | 19 ++ .../Mocks/StoreButtonAssemblyMock.swift | 24 ++ .../Mocks/StoreButtonsAssemblyMock.swift | 25 ++ .../Mocks/SubscriptionsPresenterMock.swift | 60 ++++ Tests/FlareTests/UnitTests/FlareTests.swift | 23 +- .../Models/PaymentTransactionTests.swift | 1 + .../UnitTests/Models/SKProductTests.swift | 3 +- ...achingProductsProviderDecoratorTests.swift | 2 +- .../Providers/IAPProviderTests.swift | 19 +- .../Providers/ProductProviderTests.swift | 6 +- .../Providers/PurchaseProviderTests.swift | 3 +- ...ortingProductsProviderDecoratorTests.swift | 79 +++++ .../TestHelpers/Fakes/SKProduct+Fake.swift | 5 +- .../TestHelpers/Fakes/StoreProduct+Fake.swift | 13 - .../Helpers/PurchaseManagerTestHelper.swift | 5 +- .../TestHelpers/Mocks/IAPProviderMock.swift | 21 +- .../Mocks/PaymentTransactionMock.swift | 46 --- .../Mocks/ProductProviderMock.swift | 12 +- .../Mocks/PurchaseProviderMock.swift | 9 +- ...{ProductMock.swift => SKProductMock.swift} | 2 +- .../Extensions/ArrayExtensionsTests.swift | 20 ++ ...bscriptionPriceViewModelFactoryTests.swift | 94 ++++++ .../SubscriptionView.ViewModel+Fake.swift | 20 ++ .../UnitTests/Helpers/XCTestCase+.swift | 35 ++ .../UnitTests/Helpers/XCTestCase+Wait.swift | 22 ++ .../Mocks/DateComponentsFormatterMock.swift | 45 +++ .../UnitTests/Mocks/ProductFetcherMock.swift | 24 ++ .../Mocks/ProductPurchaseServiceMock.swift | 27 ++ ...ubscriptionDateComponentsFactoryMock.swift | 24 ++ ...ubscriptionPriceViewModelFactoryMock.swift | 37 +++ ...ubscriptionsViewModelViewFactoryMock.swift | 24 ++ .../Product/ProductPresenterTests.swift | 88 +++++ .../Product/ProductStrategyTests.swift | 66 ++++ .../ProductViewModelFactoryTests.swift | 103 ++++++ ...bscriptionDateComponentsFactoryTests.swift | 59 ++++ .../Products/ProductsPresenterTests.swift | 70 ++++ .../StoreButtonPresenterTests.swift | 41 +++ .../SubscriptionsPresenterTests.swift | 132 ++++++++ .../Helpers/SnapshotTestCase.swift | 102 ++++++ .../SnapshotTests/Helpers/ThemableView.swift | 24 ++ .../ProductInfoViewSnapshotTests.swift | 81 +++++ .../ProductPlaceholderViewSnapshotTests.swift | 49 +++ .../ProductViewSnapshotTests.swift | 82 +++++ .../ProductsViewSnapshotTests.swift | 86 +++++ .../SubscriptionsViewSnapshotTests.swift | 70 ++++ ...oView_compactStyle_whenIconIsNil-iOS.1.png | Bin 0 -> 21459 bytes ...iew_compactStyle_whenIconIsNil-macOS.1.png | Bin 0 -> 24663 bytes ...View_compactStyle_whenIconIsNil-tvOS.1.png | Bin 0 -> 51182 bytes ...ew_compactStyle_whenIconIsNotNil-iOS.1.png | Bin 0 -> 23833 bytes ..._compactStyle_whenIconIsNotNil-macOS.1.png | Bin 0 -> 25757 bytes ...w_compactStyle_whenIconIsNotNil-tvOS.1.png | Bin 0 -> 52972 bytes ...nfoView_largeStyle_whenIconIsNil-iOS.1.png | Bin 0 -> 44994 bytes ...View_largeStyle_whenIconIsNotNil-iOS.1.png | Bin 0 -> 47452 bytes ...ew_compactStyle_whenIconIsHidden-iOS.1.png | Bin 0 -> 8493 bytes ..._compactStyle_whenIconIsHidden-macOS.1.png | Bin 0 -> 6273 bytes ...w_compactStyle_whenIconIsHidden-tvOS.1.png | Bin 0 -> 37695 bytes ...w_compactStyle_whenIconIsVisible-iOS.1.png | Bin 0 -> 9236 bytes ...compactStyle_whenIconIsVisible-macOS.1.png | Bin 0 -> 6631 bytes ..._compactStyle_whenIconIsVisible-tvOS.1.png | Bin 0 -> 39310 bytes ...View_largeStyle_whenIconIsHidden-iOS.1.png | Bin 0 -> 30889 bytes ...iew_largeStyle_whenIconIsVisible-iOS.1.png | Bin 0 -> 32883 bytes ...aceholderView_whenIconIsHidden-macOS.1.png | Bin 0 -> 6273 bytes ...laceholderView_whenIconIsHidden-tvOS.1.png | Bin 0 -> 37695 bytes ...ceholderView_whenIconIsVisible-macOS.1.png | Bin 0 -> 6634 bytes ...aceholderView_whenIconIsVisible-tvOS.1.png | Bin 0 -> 39310 bytes ..._productView_customStyle_product-iOS.1.png | Bin 0 -> 74775 bytes ...roductView_customStyle_product-macOS.1.png | Bin 0 -> 40559 bytes ...productView_customStyle_product-tvOS.1.png | Bin 0 -> 48022 bytes .../test_productView_error-iOS.1.png | Bin 0 -> 58552 bytes .../test_productView_error-macOS.1.png | Bin 0 -> 25558 bytes .../test_productView_error-tvOS.1.png | Bin 0 -> 37695 bytes .../test_productView_loading-iOS.1.png | Bin 0 -> 58552 bytes .../test_productView_loading-macOS.1.png | Bin 0 -> 25558 bytes .../test_productView_loading-tvOS.1.png | Bin 0 -> 37695 bytes .../test_productView_product-iOS.1.png | Bin 0 -> 68796 bytes .../test_productView_product-macOS.1.png | Bin 0 -> 41861 bytes .../test_productView_product-tvOS.1.png | Bin 0 -> 48468 bytes .../test_productsView_error-iOS.1.png | Bin 0 -> 81916 bytes .../test_productsView_error-macOS.1.png | Bin 0 -> 179651 bytes .../test_productsView_error-tvOS.1.png | Bin 0 -> 54142 bytes .../test_productsView_products-iOS.1.png | Bin 0 -> 65031 bytes .../test_productsView_products-macOS.1.png | Bin 0 -> 155791 bytes .../test_productsView_products-tvOS.1.png | Bin 0 -> 42100 bytes ...View_products_withRestoreButtons-iOS.1.png | Bin 0 -> 74404 bytes ...ew_products_withRestoreButtons-macOS.1.png | Bin 0 -> 161772 bytes ...iew_products_withRestoreButtons-tvOS.1.png | Bin 0 -> 50562 bytes ...st_subscriptionsView_customStyle-iOS.1.png | Bin 0 -> 105315 bytes ...t_subscriptionsView_customStyle-tvOS.1.png | Bin 0 -> 72049 bytes ...t_subscriptionsView_defaultStyle-iOS.1.png | Bin 0 -> 154178 bytes ..._subscriptionsView_defaultStyle-tvOS.1.png | Bin 0 -> 67971 bytes Tests/TestPlans/FlareUIUnitTests.xctestplan | 44 +++ Tests/TestPlans/SnapshotTests.xctestplan | 37 +++ project.yml | 85 ++++- 358 files changed, 11453 insertions(+), 240 deletions(-) create mode 100644 .github/actions/build_and_test/action.yml create mode 100644 .github/actions/upload_test_coverage_report/action.yml rename .github/workflows/{ci.yml => flare.yml} (70%) create mode 100644 .github/workflows/flare_ui.yml create mode 100644 .github/workflows/swiftlint.yml create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/Flare-Package.xcscheme create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/FlareUI.xcscheme create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/FlareUITests.xcscheme create mode 100644 Sources/Flare/Classes/Extensions/Product.SubscriptionInfo.Status+ISubscriptionInfoStatus.swift create mode 100644 Sources/Flare/Classes/Listeners/TransactionListener/TransactionListenerDelegate.swift create mode 100644 Sources/Flare/Classes/Models/ExpirationReason.swift create mode 100644 Sources/Flare/Classes/Models/Internal/Protocols/IRenewalInfo.swift create mode 100644 Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfo.swift create mode 100644 Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfoStatus.swift create mode 100644 Sources/Flare/Classes/Models/Internal/SK2RenewalInfo.swift create mode 100644 Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfo.swift create mode 100644 Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfoStatus.swift create mode 100644 Sources/Flare/Classes/Models/PriceIncreaseStatus.swift create mode 100644 Sources/Flare/Classes/Models/RenewalInfo.swift create mode 100644 Sources/Flare/Classes/Models/RenewalState.swift create mode 100644 Sources/Flare/Classes/Models/SubscriptionInfo.swift create mode 100644 Sources/Flare/Classes/Models/SubscriptionInfoStatus.swift create mode 100644 Sources/Flare/Classes/Models/VerificationResult.swift rename Sources/Flare/Classes/Providers/ProductProvider/Decorators/{ProductsCacheProviderDecorator => CachingProductsProviderDecorator}/CachingProductsProviderDecorator.swift (87%) rename Sources/Flare/Classes/Providers/ProductProvider/Decorators/{ProductsCacheProviderDecorator => CachingProductsProviderDecorator}/ICachingProductsProviderDecorator.swift (100%) create mode 100644 Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/ISortingProductsProviderDecorator.swift create mode 100644 Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/SortingProductsProviderDecorator.swift create mode 100644 Sources/Flare/Makefile rename swiftgen.yml => Sources/Flare/swiftgen.yml (69%) create mode 100644 Sources/FlareMock/Fakes/StoreProduct+Fake.swift create mode 100644 Sources/FlareMock/Fakes/StoreTransaction+Fake.swift create mode 100644 Sources/FlareMock/Mocks/PaymentTransactionMock.swift create mode 100644 Sources/FlareMock/Mocks/ProductMock.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/AnyProductStyle.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/AnySubscriptionControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/ProductAssemblyKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/StoreButtonsAssemblyKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/BlurEffectStyleKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/PoliciesButtonStyleKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/ProductStyleKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseCompletionKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseOptionKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonViewFontWeightKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionBackgroundKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionControlStyleKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionHeaderContentBackgroundKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionMarketingContentKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPickerItemBackgroundKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyDestinationKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyURLKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionStoreButtonLabelKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceDestinationKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceURLKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionViewTintKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionsWrapperViewStyleKey.swift create mode 100644 Sources/FlareUI/Classes/Core/EnvironmentKey/TintColorKey.swift create mode 100644 Sources/FlareUI/Classes/Core/Extensions/Array+RemoveDuplicates.swift create mode 100644 Sources/FlareUI/Classes/Core/Extensions/String+SubSequence.swift create mode 100644 Sources/FlareUI/Classes/Core/Extensions/StringProtocol+Words.swift create mode 100644 Sources/FlareUI/Classes/Core/Extensions/View+EraseToAnyView.swift create mode 100644 Sources/FlareUI/Classes/Core/Formatters/DateComponentsFormatter+Full.swift create mode 100644 Sources/FlareUI/Classes/Core/Formatters/IDateComponentsFormatter.swift create mode 100644 Sources/FlareUI/Classes/Core/Helpers/Array+StoreProduct.swift create mode 100644 Sources/FlareUI/Classes/Core/Helpers/Color+UIColor.swift create mode 100644 Sources/FlareUI/Classes/Core/Helpers/Error+IAP.swift create mode 100644 Sources/FlareUI/Classes/Core/Helpers/Value.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/Internal/PriceDisplayFormat.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/Internal/ProductStyle.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/PaywallType.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/PurchaseOptions.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/SubscptionType.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/SubscriptionStatusVerifierType.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/SubscriptionStoreButtonLabel.swift create mode 100644 Sources/FlareUI/Classes/Core/Models/UIConfiguration.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/ConfigurationProvider.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/IConfigurationProvider.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/ISubscriptionStatusVerifierProvider.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/SubscriptionStatusVerifierProvider.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/ISubscriptionStatusVerifier.swift create mode 100644 Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/SubscriptionStatusVerifier.swift create mode 100644 Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/ISubscriptionStatusVerifierTypeResolver.swift create mode 100644 Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/SubscriptionStatusVerifierTypeResolver.swift create mode 100644 Sources/FlareUI/Classes/DI/Dependencies/FlareDependencies.swift create mode 100644 Sources/FlareUI/Classes/DI/Dependencies/IFlareDependencies.swift create mode 100644 Sources/FlareUI/Classes/DI/PresentationAssembly/IPresentationAssembly.swift create mode 100644 Sources/FlareUI/Classes/DI/PresentationAssembly/PresentationAssembly.swift create mode 100644 Sources/FlareUI/Classes/FlareUI.swift create mode 100644 Sources/FlareUI/Classes/Generated/Colors.swift create mode 100644 Sources/FlareUI/Classes/Generated/Media.swift create mode 100644 Sources/FlareUI/Classes/Generated/Strings.swift create mode 100644 Sources/FlareUI/Classes/IFlareUI.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/BaseHostingController/BaseHostingController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/ColorRepresentation.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/SUIViewWrapper.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewControllerViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewControllerViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewControllerViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/HostingController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/ViewController.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Constants/Palette.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Constants/UIConstants.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonType.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonVisibility.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IPresenter.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Factories/ISubscriptionPriceViewModelFactory.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Factories/SubscriptionPriceViewModelFactory.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Contrast.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Paywall.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+ProductViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseCompletion.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseOption.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButton.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButtonViewFontWeight.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionBackground.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionHeaderContentBackground.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionMarketingContent.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPickerItemBackground.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyDestination.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyURL.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionStoreButtonLabel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceDestination.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceURL.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionViewTint.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+TintColor.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Helpers/UIKit/ViewController+Child.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/BorderedButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/PrimaryButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/CompactProductStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Configuration/ProductStyleConfiguration.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/LargeProductStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Compact.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Large.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Configuration/SubscriptionStoreControlStyleConfiguration.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+Bordered.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+PickerSubscriptionStoreControlStyle+PickerSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+ProminentPickerSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Protocols/ISubscriptionControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/AutomaticSubscriptionControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyleView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyleView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyleView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ActivityIndicatorModifier.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/BlurEffectModifier.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ErrorAlertViewModifier.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/LoadViewModifier.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/PaywallViewModifier.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Views/ActivityIndicator/ActivityIndicatorView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Views/BlurVisualEffectView/BlurVisualEffectView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Views/ImageView/ImageView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Views/ProductPlaceholderView/ProductPlaceholderView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Components/Views/SafariWebView/SafariWebView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Helpers/ViewWrapper.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Helpers/WrapperViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PaywallView/PaywallView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AnyPoliciesButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AutomaticPoliciesButtonStyle/AutomaticPoliciesButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/Configuration/PoliciesButtonStyleConfiguration.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyleView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/IPoliciesButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/TVPoliciesButtonStyle/TVPoliciesButtonStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Views/PoliciesUnavailableView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPresenter.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPurchaseService.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModelFactory.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewType.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductWrapperView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/Strategies/ProductStrategy.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/SubscriptionDateComponentsFactory.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductView/Views/ProductInfoView/ProductInfoView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsPresenter.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsWrapperView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/ProductsView/Views/StoreUnavaliableView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButton.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonPresenter.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/StoreButtonsView/StoreButtonsAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/AnySubscriptionsWrapperViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Configuration/SubscriptionsWrapperViewStyleConfiguration.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/ISubscriptionsWrapperViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Automatic/AutomaticSubscriptionsWrapperViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperViewStyle.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsAssembly.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsPresenter.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModel.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModelViewFactory.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsWrapperView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/LoadingView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionHeaderView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionToolbarView.swift create mode 100644 Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionView.swift create mode 100644 Sources/FlareUI/Makefile create mode 100644 Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/dynamic_background.colorset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/gray.colorset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/system_background.colorset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Assets.xcassets/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/checkmark.circle.fill.svg create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/circle.svg create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/Contents.json create mode 100644 Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/star.svg create mode 100644 Sources/FlareUI/Resources/Localization/en.lproj/Localizable.strings create mode 100644 Sources/FlareUI/Resources/Localization/ru.lproj/Localizable.strings create mode 100644 Sources/FlareUI/swiftgen.yml create mode 100644 Sources/FlareUIMock/Mocks/FlareMock.swift create mode 100644 Sources/FlareUIMock/Mocks/ProductPresenterMock.swift create mode 100644 Sources/FlareUIMock/Mocks/ProductViewAssemblyMock.swift create mode 100644 Sources/FlareUIMock/Mocks/ProductsPresenterMock.swift create mode 100644 Sources/FlareUIMock/Mocks/StoreButtonAssemblyMock.swift create mode 100644 Sources/FlareUIMock/Mocks/StoreButtonsAssemblyMock.swift create mode 100644 Sources/FlareUIMock/Mocks/SubscriptionsPresenterMock.swift create mode 100644 Tests/FlareTests/UnitTests/Providers/SortingProductsProviderDecoratorTests.swift delete mode 100644 Tests/FlareTests/UnitTests/TestHelpers/Fakes/StoreProduct+Fake.swift delete mode 100644 Tests/FlareTests/UnitTests/TestHelpers/Mocks/PaymentTransactionMock.swift rename Tests/FlareTests/UnitTests/TestHelpers/Mocks/{ProductMock.swift => SKProductMock.swift} (94%) create mode 100644 Tests/FlareUITests/UnitTests/Core/Extensions/ArrayExtensionsTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Core/SubscriptionPriceViewModelFactoryTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Fakes/SubscriptionView.ViewModel+Fake.swift create mode 100644 Tests/FlareUITests/UnitTests/Helpers/XCTestCase+.swift create mode 100644 Tests/FlareUITests/UnitTests/Helpers/XCTestCase+Wait.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/DateComponentsFormatterMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/ProductFetcherMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/ProductPurchaseServiceMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/SubscriptionDateComponentsFactoryMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/SubscriptionPriceViewModelFactoryMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Mocks/SubscriptionsViewModelViewFactoryMock.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Product/ProductPresenterTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Product/ProductStrategyTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Product/ProductViewModelFactoryTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Product/SubscriptionDateComponentsFactoryTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Products/ProductsPresenterTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/StoreButton/StoreButtonPresenterTests.swift create mode 100644 Tests/FlareUITests/UnitTests/Presentation/Subscriptions/SubscriptionsPresenterTests.swift create mode 100644 Tests/SnapshotTests/Helpers/SnapshotTestCase.swift create mode 100644 Tests/SnapshotTests/Helpers/ThemableView.swift create mode 100644 Tests/SnapshotTests/ProductInfoViewSnapshotTests.swift create mode 100644 Tests/SnapshotTests/ProductPlaceholderViewSnapshotTests.swift create mode 100644 Tests/SnapshotTests/ProductViewSnapshotTests.swift create mode 100644 Tests/SnapshotTests/ProductsViewSnapshotTests.swift create mode 100644 Tests/SnapshotTests/SubscriptionsViewSnapshotTests.swift create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNil-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNotNil-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_largeStyle_whenIconIsHidden-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_largeStyle_whenIconIsVisible-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsHidden-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsHidden-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsVisible-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsVisible-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_customStyle_product-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_customStyle_product-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_customStyle_product-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_error-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_error-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_error-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_loading-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_loading-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_loading-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_product-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_product-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_product-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_error-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_error-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_error-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products_withRestoreButtons-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products_withRestoreButtons-macOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products_withRestoreButtons-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_customStyle-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_customStyle-tvOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_defaultStyle-iOS.1.png create mode 100644 Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_defaultStyle-tvOS.1.png create mode 100644 Tests/TestPlans/FlareUIUnitTests.xctestplan create mode 100644 Tests/TestPlans/SnapshotTests.xctestplan diff --git a/.github/actions/build_and_test/action.yml b/.github/actions/build_and_test/action.yml new file mode 100644 index 000000000..63bc9fe15 --- /dev/null +++ b/.github/actions/build_and_test/action.yml @@ -0,0 +1,48 @@ +name: Build and Test Action + +inputs: + scheme: + required: true + type: string + destination: + required: true + type: string + name: + required: true + type: string + test_plan: + required: false + type: string + generate_project: + required: false + type: boolean + default: true + +runs: + using: "composite" + + steps: + - name: Install Dependencies & Generate project + shell: bash + run: | + if [ "${{ inputs.generate_project }}" = "true" ]; then + make setup_build_tools + make generate + fi + - name: ${{ inputs.name }} + shell: bash + run: | + if [ -n "${{ inputs.test_plan }}" ]; then + xcodebuild clean test \ + -scheme ${{ inputs.scheme }} \ + -destination "${{ inputs.destination }}" \ + -testPlan ${{ inputs.test_plan }} \ + -enableCodeCoverage YES \ + -resultBundlePath "test_output/${{ inputs.name }}.xcresult" || exit 1 + else + xcodebuild clean test \ + -scheme ${{ inputs.scheme }} \ + -destination "${{ inputs.destination }}" \ + -enableCodeCoverage YES \ + -resultBundlePath "test_output/${{ inputs.name }}.xcresult" || exit 1 + fi \ No newline at end of file diff --git a/.github/actions/upload_test_coverage_report/action.yml b/.github/actions/upload_test_coverage_report/action.yml new file mode 100644 index 000000000..3599ff664 --- /dev/null +++ b/.github/actions/upload_test_coverage_report/action.yml @@ -0,0 +1,34 @@ +name: Upload a Test Coverage Report + +inputs: + filename: + required: true + type: string + scheme_name: + required: true + type: string + token: + description: 'A CodeCov Token' + required: true + +runs: + using: "composite" + + steps: + - name: Dir + shell: bash + run: pwd && ls -al + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3.1.0 + with: + token: ${{ inputs.token }} + xcode: true + flags: ${{ inputs.scheme_name }} + xcode_archive_path: test_output/${{ inputs.filename }}.xcresult + - name: Dir + shell: bash + run: pwd && ls -al + - uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.filename }} + path: test_output \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/flare.yml similarity index 70% rename from .github/workflows/ci.yml rename to .github/workflows/flare.yml index 33acbd8d8..70e4f93fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/flare.yml @@ -9,21 +9,13 @@ on: paths: - '.swiftlint.yml' - ".github/workflows/**" + - "Package@swift-5.7.swift" + - "Package@swift-5.8.swift" - "Package.swift" - - "Source/**" + - "Source/Flare/**" - "Tests/**" jobs: - SwiftLint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: GitHub Action for SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 - with: - args: --strict - env: - DIFF_BASE: ${{ github.base_ref }} macOS: name: ${{ matrix.name }} runs-on: ${{ matrix.runsOn }} @@ -49,17 +41,18 @@ jobs: steps: - uses: actions/checkout@v3 - name: ${{ matrix.name }} - run: xcodebuild test -scheme "Flare" -destination "platform=macOS" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 + uses: ./.github/actions/build_and_test with: + scheme: Flare + destination: "platform=macOS" name: ${{ matrix.name }} - path: test_output + generate_project: false + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: Flare + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} iOS: name: ${{ matrix.name }} @@ -81,16 +74,19 @@ jobs: runsOn: macos-13 steps: - uses: actions/checkout@v3 - - name: Install Dependencies - run: make setup_build_tools - - name: Generate project - run: make generate - name: ${{ matrix.name }} - run: xcodebuild test -scheme "Flare" -destination "${{ matrix.destination }}" -testPlan AllTests clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - uses: actions/upload-artifact@v4 + uses: ./.github/actions/build_and_test with: + scheme: Flare + destination: ${{ matrix.destination }} name: ${{ matrix.name }} - path: test_output + test_plan: AllTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: Flare + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} tvOS: name: ${{ matrix.name }} @@ -112,22 +108,19 @@ jobs: runsOn: macos-13 steps: - uses: actions/checkout@v3 - - name: Install Dependencies - run: make setup_build_tools - - name: Generate project - run: make generate - name: ${{ matrix.name }} - run: xcodebuild test -scheme "Flare" -destination "${{ matrix.destination }}" -testPlan AllTests clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 + uses: ./.github/actions/build_and_test with: + scheme: Flare + destination: ${{ matrix.destination }} name: ${{ matrix.name }} - path: test_output + test_plan: AllTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: Flare + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} watchOS: name: ${{ matrix.name }} @@ -153,22 +146,19 @@ jobs: runsOn: macos-13 steps: - uses: actions/checkout@v3 - - name: Install Dependencies - run: make setup_build_tools - - name: Generate project - run: make generate - name: ${{ matrix.name }} - run: xcodebuild test -scheme "Flare" -destination "${{ matrix.destination }}" -testPlan UnitTests clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 - with: - token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: test_output/${{ matrix.name }}.xcresult - - uses: actions/upload-artifact@v4 + uses: ./.github/actions/build_and_test with: + scheme: Flare + destination: ${{ matrix.destination }} name: ${{ matrix.name }} - path: test_output + test_plan: UnitTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: Flare + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} spm: name: ${{ matrix.name }} diff --git a/.github/workflows/flare_ui.yml b/.github/workflows/flare_ui.yml new file mode 100644 index 000000000..306d3012e --- /dev/null +++ b/.github/workflows/flare_ui.yml @@ -0,0 +1,267 @@ +name: "flare-ui" + +on: + push: + branches: + - main + - dev + pull_request: + paths: + - '.swiftlint.yml' + - ".github/workflows/**" + - "Package@swift-5.7.swift" + - "Package@swift-5.8.swift" + - "Package.swift" + - "Source/FlareUI/**" + - "Tests/FlareUITests/**" + +jobs: + macOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - xcode: "Xcode_15.0" + runsOn: macos-13 + name: "macOS 13, Xcode 15.0, Swift 5.9.0" + - xcode: "Xcode_14.3.1" + runsOn: macos-13 + name: "macOS 13, Xcode 14.3.1, Swift 5.8.0" + - xcode: "Xcode_14.2" + runsOn: macOS-12 + name: "macOS 12, Xcode 14.2, Swift 5.7.2" + - xcode: "Xcode_14.1" + runsOn: macOS-12 + name: "macOS 12, Xcode 14.1, Swift 5.7.1" + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + uses: ./.github/actions/build_and_test + with: + scheme: FlareUI + destination: "platform=macOS" + name: ${{ matrix.name }} + generate_project: false + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: FlareUI + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + iOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0.1,name=iPhone 14 Pro" + name: "iOS 17.0.1" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=iPhone 14 Pro" + name: "iOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + uses: ./.github/actions/build_and_test + with: + scheme: FlareUI + destination: ${{ matrix.destination }} + name: ${{ matrix.name }} + test_plan: FlareUIUnitTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: FlareUI + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + tvOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=17.0,name=Apple TV" + name: "tvOS 17.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=Apple TV" + name: "tvOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + uses: ./.github/actions/build_and_test + with: + scheme: FlareUI + destination: ${{ matrix.destination }} + name: ${{ matrix.name }} + test_plan: FlareUIUnitTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: FlareUI + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + watchOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=10.0,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=9.4,name=Apple Watch Series 8 (45mm)" + name: "watchOS 9.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + - destination: "OS=8.5,name=Apple Watch Series 7 (45mm)" + name: "watchOS 8.5" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + uses: ./.github/actions/build_and_test + with: + scheme: FlareUI + destination: ${{ matrix.destination }} + name: ${{ matrix.name }} + test_plan: FlareUIUnitTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: FlareUI + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + spm: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - name: "Xcode 15" + xcode: "Xcode_15.0" + runsOn: macos-13 + - name: "Xcode 14" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + run: swift build -c release --target FlareUI + + snapshots: + name: snapshots / ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + # - destination: "platform=macOS" + # xcode: "Xcode_15.0" + # runsOn: macos-13 + # name: "macOS 13, Xcode 15.0, Swift 5.9.0" + - destination: "OS=17.2,name=iPhone 15" + name: "iOS 17.2" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=17.2,name=Apple TV" + name: "tvOS 17.2" + xcode: "Xcode_15.0" + runsOn: macos-13 + steps: + - uses: actions/checkout@v3 + - name: ${{ matrix.name }} + uses: ./.github/actions/build_and_test + with: + scheme: FlareUI + destination: ${{ matrix.destination }} + name: ${{ matrix.name }}SnapshotTests + test_plan: SnapshotTests + - name: Upload test coverage reports to Codecov + uses: ./.github/actions/upload_test_coverage_report + with: + scheme_name: FlareUI + filename: ${{ matrix.name }}SnapshotTests + token: ${{ secrets.CODECOV_TOKEN }} + + merge-test-reports: + needs: [iOS, macOS, watchOS, tvOS] + runs-on: macos-13 + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: test_output + - run: xcrun xcresulttool merge test_output/**/*.xcresult --output-path test_output/final/final.xcresult + - name: Upload Merged Artifact + uses: actions/upload-artifact@v4 + with: + name: MergedResult + path: test_output/final + + discover-typos: + name: Discover Typos + runs-on: macOS-12 + env: + DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer + steps: + - uses: actions/checkout@v2 + - name: Discover typos + run: | + export PATH="$PATH:/Library/Frameworks/Python.framework/Versions/3.11/bin" + python3 -m pip install --upgrade pip + python3 -m pip install codespell + codespell --ignore-words-list="hart,inout,msdos,sur" --skip="./.build/*,./.git/*" + + # Beta: + # name: ${{ matrix.name }} + # runs-on: firebreak + # env: + # DEVELOPER_DIR: "/Applications/Xcode_15.0.app/Contents/Developer" + # timeout-minutes: 10 + # strategy: + # fail-fast: false + # matrix: + # include: + # - destination: "OS=1.0,name=Apple Vision Pro" + # name: "visionOS 1.0" + # scheme: "Flare" + # steps: + # - uses: actions/checkout@v3 + # - name: ${{ matrix.name }} + # run: xcodebuild test -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" clean || exit 1 \ No newline at end of file diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml new file mode 100644 index 000000000..4d09696c0 --- /dev/null +++ b/.github/workflows/swiftlint.yml @@ -0,0 +1,26 @@ +name: "swiftlint" + +on: + push: + branches: + - main + - dev + pull_request: + paths: + - '.swiftlint.yml' + - ".github/workflows/**" + - "Package@swift-5.7.swift" + - "Package@swift-5.8.swift" + - "Package.swift" + - "Source/**" + - "Tests/**" + +jobs: + SwiftLint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: GitHub Action for SwiftLint + uses: norio-nomura/action-swiftlint@3.2.1 + with: + args: lint --config ./.swiftlint.yml --strict \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5ca27c72b..a47bd1257 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,6 @@ playground.xcworkspace Carthage/Build/ # Accio dependency management -Dependencies/ .accio/ # fastlane @@ -88,4 +87,5 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ -*.xcodeproj \ No newline at end of file +*.xcodeproj +Example/ \ No newline at end of file diff --git a/.swiftformat b/.swiftformat index a4294246c..8af055a0b 100644 --- a/.swiftformat +++ b/.swiftformat @@ -33,7 +33,6 @@ --enable redundantPattern --enable redundantRawValues --enable redundantReturn ---enable redundantSelf --enable redundantVoidReturnType --enable semicolons --enable sortImports @@ -57,6 +56,8 @@ --enable markTypes --enable isEmpty +--disable redundantSelf + # format options --wraparguments before-first diff --git a/.swiftlint.yml b/.swiftlint.yml index 530b2f63f..4f6c54e63 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -4,6 +4,13 @@ excluded: - Package@swift-5.7.swift - Package@swift-5.8.swift - Sources/Flare/Classes/Generated/Strings.swift + - Sources/FlareUI/Classes/Generated/Strings.swift + - Sources/FlareUI/Classes/Generated/Colors.swift + - Sources/FlareUI/Classes/Generated/Media.swift + - Sources/FlareUI/Classes/Presentation/Components/Core/Constants/UIConstants.swift + - Sources/FlareUIMock/Mocks/ + - Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/ViewController.swift + - Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/HostingController.swift - .build # Rules @@ -16,7 +23,6 @@ disabled_rules: opt_in_rules: # some rules are only opt-in - anyobject_protocol - - array_init - closure_body_length - closure_end_indentation - closure_spacing @@ -136,4 +142,4 @@ type_name: error: 50 file_name: - excluded: ["Types.swift"] \ No newline at end of file + excluded: ["Types.swift"] diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Flare-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Flare-Package.xcscheme new file mode 100644 index 000000000..c40d994ec --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Flare-Package.xcscheme @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Flare.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Flare.xcscheme index 866e55a53..b12f0a4ad 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Flare.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Flare.xcscheme @@ -34,20 +34,6 @@ ReferencedContainer = "container:"> - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/FlareUITests.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/FlareUITests.xcscheme new file mode 100644 index 000000000..bdde5ca5d --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/FlareUITests.xcscheme @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 68c0d2b2d..81d4ebef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] ## Added +- Implement the `FlareUI` package + - Added in Pull Request [#28](https://github.com/space-code/flare/pull/28). - Implement asynchronous transaction completion - Added in Pull Request [#25](https://github.com/space-code/flare/pull/25). diff --git a/Makefile b/Makefile index 37e687ede..c9a6e5111 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +CHILD_MAKEFILES_DIRS = $(sort $(dir $(wildcard Sources/*/Makefile))) + all: bootstrap bootstrap: hook @@ -16,8 +18,8 @@ lint: fmt: mint run swiftformat Sources Tests -strings: - swiftgen +swiftgen: + @for d in $(CHILD_MAKEFILES_DIRS); do ( cd $$d && make swiftgen; ); done generate: xcodegen generate diff --git a/Package.resolved b/Package.resolved index afc089a5b..a4a420e1b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -44,6 +44,24 @@ "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", "version" : "1.0.0" } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "e7b77228b34057041374ebef00c0fd7739d71a2b", + "version" : "1.15.3" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", + "version" : "509.1.1" + } } ], "version" : 2 diff --git a/Package.swift b/Package.swift index 0291e2f6c..66e7786f8 100644 --- a/Package.swift +++ b/Package.swift @@ -8,6 +8,7 @@ let visionOSSetting: SwiftSetting = .define("VISION_OS", .when(platforms: [.visi let package = Package( name: "Flare", + defaultLocalization: "en", platforms: [ .macOS(.v10_15), .iOS(.v13), @@ -17,12 +18,17 @@ let package = Package( ], products: [ .library(name: "Flare", targets: ["Flare"]), + .library(name: "FlareUI", targets: ["FlareUI"]), ], dependencies: [ .package(url: "https://github.com/space-code/concurrency.git", .upToNextMajor(from: "0.0.1")), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"), .package(url: "https://github.com/space-code/log.git", .upToNextMajor(from: "1.1.0")), .package(url: "https://github.com/space-code/atomic.git", .upToNextMajor(from: "1.0.0")), + .package( + url: "https://github.com/pointfreeco/swift-snapshot-testing", + from: "1.15.3" + ), ], targets: [ .target( @@ -35,12 +41,36 @@ let package = Package( resources: [.process("Resources")], swiftSettings: [visionOSSetting] ), + .target( + name: "FlareUI", + dependencies: ["Flare"], + resources: [.process("Resources")] + ), + .target(name: "FlareMock", dependencies: ["Flare"]), + .target(name: "FlareUIMock", dependencies: ["FlareMock", "FlareUI"]), .testTarget( name: "FlareTests", dependencies: [ "Flare", + "FlareMock", .product(name: "TestConcurrency", package: "concurrency"), ] ), + .testTarget( + name: "FlareUITests", + dependencies: [ + "FlareUI", + "FlareMock", + "FlareUIMock", + ] + ), + .testTarget( + name: "SnapshotTests", + dependencies: [ + "Flare", + "FlareUIMock", + .product(name: "SnapshotTesting", package: "swift-snapshot-testing"), + ] + ), ] ) diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift index 693b183aa..d490e62d7 100644 --- a/Package@swift-5.7.swift +++ b/Package@swift-5.7.swift @@ -6,6 +6,7 @@ import PackageDescription let package = Package( name: "Flare", + defaultLocalization: "en", platforms: [ .macOS(.v10_15), .iOS(.v13), @@ -14,12 +15,17 @@ let package = Package( ], products: [ .library(name: "Flare", targets: ["Flare"]), + .library(name: "FlareUI", targets: ["FlareUI"]), ], dependencies: [ .package(url: "https://github.com/space-code/concurrency.git", .upToNextMajor(from: "0.0.1")), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"), .package(url: "https://github.com/space-code/log.git", .upToNextMajor(from: "1.1.0")), .package(url: "https://github.com/space-code/atomic.git", .upToNextMajor(from: "1.0.0")), + .package( + url: "https://github.com/pointfreeco/swift-snapshot-testing", + from: "1.15.3" + ), ], targets: [ .target( @@ -31,12 +37,36 @@ let package = Package( ], resources: [.process("Resources")] ), + .target( + name: "FlareUI", + dependencies: ["Flare"], + resources: [.process("Resources")] + ), + .target(name: "FlareMock", dependencies: ["Flare"]), + .target(name: "FlareUIMock", dependencies: ["FlareMock", "FlareUI"]), .testTarget( name: "FlareTests", dependencies: [ "Flare", + "FlareMock", .product(name: "TestConcurrency", package: "concurrency"), ] ), + .testTarget( + name: "FlareUITests", + dependencies: [ + "FlareUI", + "FlareMock", + "FlareUIMock", + ] + ), + .testTarget( + name: "SnapshotTests", + dependencies: [ + "Flare", + "FlareUIMock", + .product(name: "SnapshotTesting", package: "swift-snapshot-testing"), + ] + ), ] ) diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift index 87183860a..7174b9d90 100644 --- a/Package@swift-5.8.swift +++ b/Package@swift-5.8.swift @@ -6,6 +6,7 @@ import PackageDescription let package = Package( name: "Flare", + defaultLocalization: "en", platforms: [ .macOS(.v10_15), .iOS(.v13), @@ -14,12 +15,17 @@ let package = Package( ], products: [ .library(name: "Flare", targets: ["Flare"]), + .library(name: "FlareUI", targets: ["FlareUI"]), ], dependencies: [ .package(url: "https://github.com/space-code/concurrency.git", .upToNextMajor(from: "0.0.1")), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0"), .package(url: "https://github.com/space-code/log.git", .upToNextMajor(from: "1.1.0")), .package(url: "https://github.com/space-code/atomic.git", .upToNextMajor(from: "1.0.0")), + .package( + url: "https://github.com/pointfreeco/swift-snapshot-testing", + from: "1.15.3" + ), ], targets: [ .target( @@ -31,12 +37,36 @@ let package = Package( ], resources: [.process("Resources")] ), + .target( + name: "FlareUI", + dependencies: ["Flare"], + resources: [.process("Resources")] + ), + .target(name: "FlareMock", dependencies: ["Flare"]), + .target(name: "FlareUIMock", dependencies: ["FlareMock", "FlareUI"]), .testTarget( name: "FlareTests", dependencies: [ "Flare", + "FlareMock", .product(name: "TestConcurrency", package: "concurrency"), ] ), + .testTarget( + name: "FlareUITests", + dependencies: [ + "FlareUI", + "FlareMock", + "FlareUIMock", + ] + ), + .testTarget( + name: "SnapshotTests", + dependencies: [ + "Flare", + "FlareUIMock", + .product(name: "SnapshotTesting", package: "swift-snapshot-testing"), + ] + ), ] ) diff --git a/Sources/Flare/Classes/DI/FlareDependencies.swift b/Sources/Flare/Classes/DI/FlareDependencies.swift index f97ae841c..3b78f6d2a 100644 --- a/Sources/Flare/Classes/DI/FlareDependencies.swift +++ b/Sources/Flare/Classes/DI/FlareDependencies.swift @@ -13,7 +13,7 @@ final class FlareDependencies: IFlareDependencies { lazy var iapProvider: IIAPProvider = IAPProvider( paymentQueue: SKPaymentQueue.default(), - productProvider: cachingProductProviderDecorator, + productProvider: sortingProductProviderDecorator, purchaseProvider: purchaseProvider, receiptRefreshProvider: receiptRefreshProvider, refundProvider: refundProvider, @@ -25,6 +25,12 @@ final class FlareDependencies: IFlareDependencies { // MARK: Private + private var sortingProductProviderDecorator: ISortingProductsProviderDecorator { + SortingProductsProviderDecorator( + productProvider: cachingProductProviderDecorator + ) + } + private var cachingProductProviderDecorator: ICachingProductsProviderDecorator { CachingProductsProviderDecorator( productProvider: productProvider, diff --git a/Sources/Flare/Classes/Extensions/Product.SubscriptionInfo.Status+ISubscriptionInfoStatus.swift b/Sources/Flare/Classes/Extensions/Product.SubscriptionInfo.Status+ISubscriptionInfoStatus.swift new file mode 100644 index 000000000..a83c5e878 --- /dev/null +++ b/Sources/Flare/Classes/Extensions/Product.SubscriptionInfo.Status+ISubscriptionInfoStatus.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import struct StoreKit.Product + +// MARK: - ISubscriptionInfoStatus + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +extension Product.SubscriptionInfo.Status: ISubscriptionInfoStatus { + var renewalState: RenewalState { + RenewalState(self.state) + } + + var subscriptionRenewalInfo: VerificationResult { + switch self.renewalInfo { + case let .verified(renewalInfo): + return .verified(.init(renewalInfo: renewalInfo)) + case let .unverified(renewalInfo, error): + return .unverified(.init(renewalInfo: renewalInfo), error) + } + } +} diff --git a/Sources/Flare/Classes/Flare.swift b/Sources/Flare/Classes/Flare.swift index 579366f0d..d2e5f2842 100644 --- a/Sources/Flare/Classes/Flare.swift +++ b/Sources/Flare/Classes/Flare.swift @@ -60,11 +60,11 @@ public final class Flare { // MARK: IFlare extension Flare: IFlare { - public func fetch(productIDs: Set, completion: @escaping Closure>) { + public func fetch(productIDs: some Collection, completion: @escaping Closure>) { iapProvider.fetch(productIDs: productIDs, completion: completion) } - public func fetch(productIDs: Set) async throws -> [StoreProduct] { + public func fetch(productIDs: some Collection) async throws -> [StoreProduct] { try await iapProvider.fetch(productIDs: productIDs) } @@ -140,7 +140,7 @@ extension Flare: IFlare { await iapProvider.finish(transaction: transaction) } - public func addTransactionObserver(fallbackHandler: Closure>?) { + public func addTransactionObserver(fallbackHandler: Closure>?) { iapProvider.addTransactionObserver(fallbackHandler: fallbackHandler) } @@ -153,6 +153,11 @@ extension Flare: IFlare { try await iapProvider.checkEligibility(productIDs: productIDs) } + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func restore() async throws { + try await iapProvider.restore() + } + #if os(iOS) || VISION_OS @available(iOS 15.0, *) @available(macOS, unavailable) diff --git a/Sources/Flare/Classes/IFlare.swift b/Sources/Flare/Classes/IFlare.swift index 277b3d79d..c108dd85c 100644 --- a/Sources/Flare/Classes/IFlare.swift +++ b/Sources/Flare/Classes/IFlare.swift @@ -19,7 +19,7 @@ public protocol IFlare { /// - Parameters: /// - productIDs: The list of product identifiers for which you wish to retrieve descriptions. /// - completion: The completion containing the response of retrieving products. - func fetch(productIDs: Set, completion: @escaping Closure>) + func fetch(productIDs: some Collection, completion: @escaping Closure>) /// Retrieves localized information from the App Store about a specified list of products. /// @@ -28,7 +28,7 @@ public protocol IFlare { /// - Throws: `IAPError(error:)` if the request did fail with error. /// /// - Returns: An array of products. - func fetch(productIDs: Set) async throws -> [StoreProduct] + func fetch(productIDs: some Collection) async throws -> [StoreProduct] /// Performs a purchase of a product. /// @@ -135,7 +135,7 @@ public protocol IFlare { /// The transactions array will only be synchronized with the server while the queue has observers. /// /// - Note: This may require that the user authenticate. - func addTransactionObserver(fallbackHandler: Closure>?) + func addTransactionObserver(fallbackHandler: Closure>?) /// Removes transaction observer from the payment queue. /// The transactions array will only be synchronized with the server while the queue has observers. @@ -151,6 +151,9 @@ public protocol IFlare { @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func checkEligibility(productIDs: Set) async throws -> [String: SubscriptionEligibility] + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws + #if os(iOS) || VISION_OS /// Present the refund request sheet for the specified transaction in a window scene. /// diff --git a/Sources/Flare/Classes/Listeners/TransactionListener/ITransactionListener.swift b/Sources/Flare/Classes/Listeners/TransactionListener/ITransactionListener.swift index 095c12af9..a65aba325 100644 --- a/Sources/Flare/Classes/Listeners/TransactionListener/ITransactionListener.swift +++ b/Sources/Flare/Classes/Listeners/TransactionListener/ITransactionListener.swift @@ -20,4 +20,6 @@ protocol ITransactionListener: Sendable { /// - Note: Available on iOS 15.0+, tvOS 15.0+, macOS 12.0+, watchOS 8.0+. @available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) func handle(purchaseResult: StoreKit.Product.PurchaseResult) async throws -> StoreTransaction? + + func set(delegate: TransactionListenerDelegate) async } diff --git a/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListener.swift b/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListener.swift index 1edd3e410..c57f6c1b5 100644 --- a/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListener.swift +++ b/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListener.swift @@ -19,9 +19,12 @@ actor TransactionListener { private let updates: AsyncStream private var task: Task? + private weak var delegate: TransactionListenerDelegate? + // MARK: Initialization - init(updates: S) where S.Element == TransactionResult { + init(delegate: TransactionListenerDelegate? = nil, updates: S) where S.Element == TransactionResult { + self.delegate = delegate self.updates = updates.toAsyncStream() } @@ -29,14 +32,20 @@ actor TransactionListener { private func handle( transactionResult: TransactionResult, - fromTransactionUpdate _: Bool + fromTransactionUpdate: Bool ) async throws -> StoreTransaction { switch transactionResult { case let .verified(transaction): - return StoreTransaction( + let transaction = StoreTransaction( transaction: transaction, jwtRepresentation: transactionResult.jwsRepresentation ) + + if fromTransactionUpdate { + delegate?.transactionListener(self, transactionDidUpdate: .success(transaction)) + } + + return transaction case let .unverified(transaction, verificationError): Logger.info( message: L10n.Purchase.transactionUnverified( @@ -45,9 +54,15 @@ actor TransactionListener { ) ) - throw IAPError.verification( - error: .unverified(productID: transaction.productID, error: verificationError) + let error = IAPError.verification( + error: .init(verificationError) ) + + if fromTransactionUpdate { + delegate?.transactionListener(self, transactionDidUpdate: .failure(error)) + } + + throw error } } } @@ -56,6 +71,10 @@ actor TransactionListener { @available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) extension TransactionListener: ITransactionListener { + func set(delegate: TransactionListenerDelegate) { + self.delegate = delegate + } + func listenForTransaction() async { task?.cancel() task = Task(priority: .utility) { [weak self] in diff --git a/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListenerDelegate.swift b/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListenerDelegate.swift new file mode 100644 index 000000000..10bb0880b --- /dev/null +++ b/Sources/Flare/Classes/Listeners/TransactionListener/TransactionListenerDelegate.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol TransactionListenerDelegate: AnyObject { + func transactionListener( + _ transactionListener: ITransactionListener, + transactionDidUpdate result: Result + ) +} diff --git a/Sources/Flare/Classes/Models/ExpirationReason.swift b/Sources/Flare/Classes/Models/ExpirationReason.swift new file mode 100644 index 000000000..f5ed18fcb --- /dev/null +++ b/Sources/Flare/Classes/Models/ExpirationReason.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation +import StoreKit + +// MARK: - ExpirationReason + +public enum ExpirationReason { + case autoRenewDisabled + case billingError + case didNotConsentToPriceIncrease + case productUnavailable + case unknown +} + +extension ExpirationReason { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(expirationReason: Product.SubscriptionInfo.RenewalInfo.ExpirationReason) { + switch expirationReason { + case .autoRenewDisabled: + self = .autoRenewDisabled + case .billingError: + self = .billingError + case .didNotConsentToPriceIncrease: + self = .didNotConsentToPriceIncrease + case .productUnavailable: + self = .productUnavailable + case .unknown: + self = .unknown + default: + self = .unknown + } + } +} diff --git a/Sources/Flare/Classes/Models/Internal/Protocols/IRenewalInfo.swift b/Sources/Flare/Classes/Models/Internal/Protocols/IRenewalInfo.swift new file mode 100644 index 000000000..3d48c0e93 --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/Protocols/IRenewalInfo.swift @@ -0,0 +1,46 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation +import StoreKit + +public protocol IRenewalInfo { + /// The JSON representation of the renewal information. + var jsonRepresentation: Data { get } + + /// The original transaction identifier for the subscription group. + var originalTransactionID: UInt64 { get } + + /// The currently active product identifier, or the most recently active product identifier if the + /// subscription is expired. + var currentProductID: String { get } + + /// Whether the subscription will auto renew at the end of the current billing period. + var willAutoRenew: Bool { get } + + /// The product identifier the subscription will auto renew to at the end of the current billing period. + /// + /// If the user disabled auto renewing, this property will be `nil`. + var autoRenewPreference: String? { get } + + /// The reason the subscription expired. + var expirationReason: ExpirationReason? { get } + + /// The status of a price increase for the user. + var priceIncreaseStatus: PriceIncreaseStatus { get } + + /// Whether the subscription is in a billing retry period. + var isInBillingRetry: Bool { get } + + /// The date the billing grace period will expire. + var gracePeriodExpirationDate: Date? { get } + + /// Identifies the offer that will be applied to the next billing period. + /// + /// If `offerType` is `promotional`, this will be the offer identifier. If `offerType` is + /// `code`, this will be the offer code reference name. This will be `nil` for `introductory` + /// offers and if there will be no offer applied for the next billing period. + var offerID: String? { get } +} diff --git a/Sources/Flare/Classes/Models/Internal/Protocols/ISKProduct.swift b/Sources/Flare/Classes/Models/Internal/Protocols/ISKProduct.swift index d649481ef..95f96964e 100644 --- a/Sources/Flare/Classes/Models/Internal/Protocols/ISKProduct.swift +++ b/Sources/Flare/Classes/Models/Internal/Protocols/ISKProduct.swift @@ -42,4 +42,7 @@ protocol ISKProduct { /// The subscription group identifier. var subscriptionGroupIdentifier: String? { get } + + /// The subscription info. + var subscription: SubscriptionInfo? { get } } diff --git a/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfo.swift b/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfo.swift new file mode 100644 index 000000000..8b8d59e65 --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfo.swift @@ -0,0 +1,10 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol ISubscriptionInfo { + var subscriptionStatus: [SubscriptionInfoStatus] { get async throws } +} diff --git a/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfoStatus.swift b/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfoStatus.swift new file mode 100644 index 000000000..7ed14f1a5 --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/Protocols/ISubscriptionInfoStatus.swift @@ -0,0 +1,11 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol ISubscriptionInfoStatus { + var renewalState: RenewalState { get } + var subscriptionRenewalInfo: VerificationResult { get } +} diff --git a/Sources/Flare/Classes/Models/Internal/SK1StoreProduct.swift b/Sources/Flare/Classes/Models/Internal/SK1StoreProduct.swift index 44722d38a..a118e5bb7 100644 --- a/Sources/Flare/Classes/Models/Internal/SK1StoreProduct.swift +++ b/Sources/Flare/Classes/Models/Internal/SK1StoreProduct.swift @@ -77,4 +77,8 @@ extension SK1StoreProduct: ISKProduct { var subscriptionGroupIdentifier: String? { product.subscriptionGroupIdentifier } + + var subscription: SubscriptionInfo? { + nil + } } diff --git a/Sources/Flare/Classes/Models/Internal/SK2RenewalInfo.swift b/Sources/Flare/Classes/Models/Internal/SK2RenewalInfo.swift new file mode 100644 index 000000000..03e8387ba --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/SK2RenewalInfo.swift @@ -0,0 +1,69 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +// MARK: - SK2RenewalInfo + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +struct SK2RenewalInfo { + // MARK: Properties + + let underlyingRenewalInfo: Product.SubscriptionInfo.RenewalInfo + + // MARK: Initialization + + init(underlyingRenewalInfo: Product.SubscriptionInfo.RenewalInfo) { + self.underlyingRenewalInfo = underlyingRenewalInfo + } +} + +// MARK: IRenewalInfo + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +extension SK2RenewalInfo: IRenewalInfo { + var jsonRepresentation: Data { + underlyingRenewalInfo.jsonRepresentation + } + + var originalTransactionID: UInt64 { + underlyingRenewalInfo.originalTransactionID + } + + var willAutoRenew: Bool { + underlyingRenewalInfo.willAutoRenew + } + + var autoRenewPreference: String? { + underlyingRenewalInfo.autoRenewPreference + } + + var isInBillingRetry: Bool { + underlyingRenewalInfo.isInBillingRetry + } + + var gracePeriodExpirationDate: Date? { + underlyingRenewalInfo.gracePeriodExpirationDate + } + + var offerID: String? { + underlyingRenewalInfo.offerID + } + + var currentProductID: String { + underlyingRenewalInfo.currentProductID + } + + var expirationReason: ExpirationReason? { + guard let expirationReason = self.underlyingRenewalInfo.expirationReason else { + return nil + } + return ExpirationReason(expirationReason: expirationReason) + } + + var priceIncreaseStatus: PriceIncreaseStatus { + PriceIncreaseStatus(underlyingRenewalInfo.priceIncreaseStatus) + } +} diff --git a/Sources/Flare/Classes/Models/Internal/SK2StoreProduct.swift b/Sources/Flare/Classes/Models/Internal/SK2StoreProduct.swift index ad99a8987..d9a1d4e46 100644 --- a/Sources/Flare/Classes/Models/Internal/SK2StoreProduct.swift +++ b/Sources/Flare/Classes/Models/Internal/SK2StoreProduct.swift @@ -85,4 +85,11 @@ extension SK2StoreProduct: ISKProduct { var subscriptionGroupIdentifier: String? { product.subscription?.subscriptionGroupID } + + var subscription: SubscriptionInfo? { + guard let subscription = product.subscription else { + return nil + } + return SubscriptionInfo(subscriptionInfo: subscription) + } } diff --git a/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfo.swift b/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfo.swift new file mode 100644 index 000000000..92081fa8c --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfo.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +// MARK: - SK2SubscriptionInfo + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +struct SK2SubscriptionInfo { + // MARK: Properties + + private let underlyingInfo: Product.SubscriptionInfo + + // MARK: Initialization + + init(underlyingInfo: Product.SubscriptionInfo) { + self.underlyingInfo = underlyingInfo + } +} + +// MARK: ISubscriptionInfo + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +extension SK2SubscriptionInfo: ISubscriptionInfo { + var subscriptionStatus: [SubscriptionInfoStatus] { + get async throws { + try await self.underlyingInfo.status.map { SubscriptionInfoStatus(underlyingStatus: $0) } + } + } +} diff --git a/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfoStatus.swift b/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfoStatus.swift new file mode 100644 index 000000000..b438ba9c6 --- /dev/null +++ b/Sources/Flare/Classes/Models/Internal/SK2SubscriptionInfoStatus.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +// MARK: - SK2SubscriptionInfoStatus + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +struct SK2SubscriptionInfoStatus { + // MARK: Properties + + let underlyingStatus: Product.SubscriptionInfo.Status + + // MARK: Initialization + + init(underlyingStatus: Product.SubscriptionInfo.Status) { + self.underlyingStatus = underlyingStatus + } +} + +// MARK: ISubscriptionInfoStatus + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +extension SK2SubscriptionInfoStatus: ISubscriptionInfoStatus { + var renewalState: RenewalState { + underlyingStatus.renewalState + } + + var subscriptionRenewalInfo: VerificationResult { + underlyingStatus.subscriptionRenewalInfo + } +} diff --git a/Sources/Flare/Classes/Models/PriceIncreaseStatus.swift b/Sources/Flare/Classes/Models/PriceIncreaseStatus.swift new file mode 100644 index 000000000..1a02c8f21 --- /dev/null +++ b/Sources/Flare/Classes/Models/PriceIncreaseStatus.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation +import StoreKit + +// MARK: - PriceIncreaseStatus + +public enum PriceIncreaseStatus { + case noIncreasePending + case pending + case agreed +} + +extension PriceIncreaseStatus { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(_ status: Product.SubscriptionInfo.RenewalInfo.PriceIncreaseStatus) { + switch status { + case .noIncreasePending: + self = .noIncreasePending + case .pending: + self = .pending + case .agreed: + self = .agreed + } + } +} diff --git a/Sources/Flare/Classes/Models/RenewalInfo.swift b/Sources/Flare/Classes/Models/RenewalInfo.swift new file mode 100644 index 000000000..acbd00fa5 --- /dev/null +++ b/Sources/Flare/Classes/Models/RenewalInfo.swift @@ -0,0 +1,73 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +// MARK: - RenewalInfo + +public struct RenewalInfo { + // MARK: Properties + + let underlyingRenewalInfo: IRenewalInfo + + // MARK: Initialization + + init(underlyingRenewalInfo: IRenewalInfo) { + self.underlyingRenewalInfo = underlyingRenewalInfo + } +} + +// MARK: - Initialization + +extension RenewalInfo { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(renewalInfo: Product.SubscriptionInfo.RenewalInfo) { + self.init(underlyingRenewalInfo: SK2RenewalInfo(underlyingRenewalInfo: renewalInfo)) + } +} + +// MARK: IRenewalInfo + +extension RenewalInfo: IRenewalInfo { + public var jsonRepresentation: Data { + underlyingRenewalInfo.jsonRepresentation + } + + public var originalTransactionID: UInt64 { + underlyingRenewalInfo.originalTransactionID + } + + public var willAutoRenew: Bool { + underlyingRenewalInfo.willAutoRenew + } + + public var autoRenewPreference: String? { + underlyingRenewalInfo.autoRenewPreference + } + + public var isInBillingRetry: Bool { + underlyingRenewalInfo.isInBillingRetry + } + + public var gracePeriodExpirationDate: Date? { + underlyingRenewalInfo.gracePeriodExpirationDate + } + + public var offerID: String? { + underlyingRenewalInfo.offerID + } + + public var currentProductID: String { + underlyingRenewalInfo.currentProductID + } + + public var expirationReason: ExpirationReason? { + underlyingRenewalInfo.expirationReason + } + + public var priceIncreaseStatus: PriceIncreaseStatus { + underlyingRenewalInfo.priceIncreaseStatus + } +} diff --git a/Sources/Flare/Classes/Models/RenewalState.swift b/Sources/Flare/Classes/Models/RenewalState.swift new file mode 100644 index 000000000..ed863df6e --- /dev/null +++ b/Sources/Flare/Classes/Models/RenewalState.swift @@ -0,0 +1,35 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +public enum RenewalState { + case subscribed + case expired + case inBillingRetryPeriod + case revoked + case inGracePeriod + case unknown + + // MARK: Initialization + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(_ state: Product.SubscriptionInfo.RenewalState) { + switch state { + case .subscribed: + self = .subscribed + case .expired: + self = .expired + case .inBillingRetryPeriod: + self = .inBillingRetryPeriod + case .revoked: + self = .revoked + case .inGracePeriod: + self = .inGracePeriod + default: + self = .unknown + } + } +} diff --git a/Sources/Flare/Classes/Models/StoreProduct.swift b/Sources/Flare/Classes/Models/StoreProduct.swift index a299804a6..306784694 100644 --- a/Sources/Flare/Classes/Models/StoreProduct.swift +++ b/Sources/Flare/Classes/Models/StoreProduct.swift @@ -97,4 +97,8 @@ extension StoreProduct: ISKProduct { public var subscriptionGroupIdentifier: String? { product.subscriptionGroupIdentifier } + + public var subscription: SubscriptionInfo? { + product.subscription + } } diff --git a/Sources/Flare/Classes/Models/StoreTransaction.swift b/Sources/Flare/Classes/Models/StoreTransaction.swift index 0a777b45c..271af0d8d 100644 --- a/Sources/Flare/Classes/Models/StoreTransaction.swift +++ b/Sources/Flare/Classes/Models/StoreTransaction.swift @@ -49,31 +49,31 @@ extension StoreTransaction { // MARK: IStoreTransaction extension StoreTransaction: IStoreTransaction { - var productIdentifier: String { + public var productIdentifier: String { storeTransaction.productIdentifier } - var purchaseDate: Date { + public var purchaseDate: Date { storeTransaction.purchaseDate } - var hasKnownPurchaseDate: Bool { + public var hasKnownPurchaseDate: Bool { storeTransaction.hasKnownPurchaseDate } - var transactionIdentifier: String { + public var transactionIdentifier: String { storeTransaction.transactionIdentifier } - var hasKnownTransactionIdentifier: Bool { + public var hasKnownTransactionIdentifier: Bool { storeTransaction.hasKnownTransactionIdentifier } - var quantity: Int { + public var quantity: Int { storeTransaction.quantity } - var jwsRepresentation: String? { + public var jwsRepresentation: String? { storeTransaction.jwsRepresentation } diff --git a/Sources/Flare/Classes/Models/SubscriptionInfo.swift b/Sources/Flare/Classes/Models/SubscriptionInfo.swift new file mode 100644 index 000000000..fa4ce37ad --- /dev/null +++ b/Sources/Flare/Classes/Models/SubscriptionInfo.swift @@ -0,0 +1,39 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +// MARK: - SubscriptionInfo + +public struct SubscriptionInfo { + // MARK: Properties + + let underlyingSubscriptionInfo: ISubscriptionInfo + + // MARK: Initialization + + init(underlyingSubscriptionInfo: ISubscriptionInfo) { + self.underlyingSubscriptionInfo = underlyingSubscriptionInfo + } +} + +// MARK: - Initializators + +extension SubscriptionInfo { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(subscriptionInfo: Product.SubscriptionInfo) { + self.init(underlyingSubscriptionInfo: SK2SubscriptionInfo(underlyingInfo: subscriptionInfo)) + } +} + +// MARK: ISubscriptionInfo + +extension SubscriptionInfo: ISubscriptionInfo { + public var subscriptionStatus: [SubscriptionInfoStatus] { + get async throws { + try await self.underlyingSubscriptionInfo.subscriptionStatus + } + } +} diff --git a/Sources/Flare/Classes/Models/SubscriptionInfoStatus.swift b/Sources/Flare/Classes/Models/SubscriptionInfoStatus.swift new file mode 100644 index 000000000..4e8650def --- /dev/null +++ b/Sources/Flare/Classes/Models/SubscriptionInfoStatus.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - SubscriptionInfoStatus + +public struct SubscriptionInfoStatus { + // MARK: Properties + + let underlyingStatus: ISubscriptionInfoStatus + + // MARK: Initialization + + init(underlyingStatus: ISubscriptionInfoStatus) { + self.underlyingStatus = underlyingStatus + } +} + +// MARK: ISubscriptionInfoStatus + +extension SubscriptionInfoStatus: ISubscriptionInfoStatus { + public var renewalState: RenewalState { + self.underlyingStatus.renewalState + } + + public var subscriptionRenewalInfo: VerificationResult { + self.underlyingStatus.subscriptionRenewalInfo + } +} diff --git a/Sources/Flare/Classes/Models/VerificationError.swift b/Sources/Flare/Classes/Models/VerificationError.swift index fc661b2fa..e732f14e1 100644 --- a/Sources/Flare/Classes/Models/VerificationError.swift +++ b/Sources/Flare/Classes/Models/VerificationError.swift @@ -4,22 +4,58 @@ // import Foundation +import StoreKit // MARK: - VerificationError /// Enumeration representing errors that can occur during verification. public enum VerificationError: Error { - // Case for unverified product with associated productID and error details. - case unverified(productID: String, error: Error) + /// The certificate chain was parsable, but was invalid due to one or more revoked certificates. + /// + /// Trying again later may retrieve valid signed data from the App Store. + case revokedCertificate + + /// The certificate chain was parsable, but it was invalid for signing this data. + case invalidCertificateChain + + /// The device verification properties were invalid for this device. + case invalidDeviceVerification + + /// Th JWS header and any data included in it or it's certificate chain had an invalid encoding. + case invalidEncoding + + /// The certificate chain was valid for signing this data, but the leaf's public key was invalid for the + /// JWS signature. + case invalidSignature + + /// Either the JWS header or any certificate in the chain was missing necessary properties for + /// verification. + case missingRequiredProperties + + /// Unknown error. + case unknown(error: Error) } -// MARK: LocalizedError +// MARK: - Initialization -extension VerificationError: LocalizedError { - public var errorDescription: String? { - switch self { - case let .unverified(productID, error): - return L10n.VerificationError.unverified(productID, error.localizedDescription) +extension VerificationError { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(_ verificationError: StoreKit.VerificationResult.VerificationError) { + switch verificationError { + case .revokedCertificate: + self = .revokedCertificate + case .invalidCertificateChain: + self = .invalidCertificateChain + case .invalidDeviceVerification: + self = .invalidDeviceVerification + case .invalidEncoding: + self = .invalidEncoding + case .invalidSignature: + self = .invalidSignature + case .missingRequiredProperties: + self = .missingRequiredProperties + @unknown default: + self = .unknown(error: verificationError) } } } diff --git a/Sources/Flare/Classes/Models/VerificationResult.swift b/Sources/Flare/Classes/Models/VerificationResult.swift new file mode 100644 index 000000000..f4123440d --- /dev/null +++ b/Sources/Flare/Classes/Models/VerificationResult.swift @@ -0,0 +1,11 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public enum VerificationResult { + case verified(SignedType) + case unverified(SignedType, Error) +} diff --git a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift index 4ffac9238..fd787f988 100644 --- a/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift +++ b/Sources/Flare/Classes/Providers/IAPProvider/IAPProvider.swift @@ -60,14 +60,18 @@ final class IAPProvider: IIAPProvider { paymentQueue.canMakePayments } - func fetch(productIDs: Set, completion: @escaping Closure>) { + func fetch(productIDs: some Collection, completion: @escaping Closure>) { if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { AsyncHandler.call( strategy: .runOnMain, completion: { (result: Result<[StoreProduct], Error>) in switch result { case let .success(products): - completion(.success(products)) + if products.isEmpty { + completion(.failure(.invalid(productIDs: Array(productIDs)))) + } else { + completion(.success(products)) + } case let .failure(error): completion(.failure(.with(error: error))) } @@ -85,7 +89,7 @@ final class IAPProvider: IIAPProvider { } } - func fetch(productIDs: Set) async throws -> [StoreProduct] { + func fetch(productIDs: some Collection) async throws -> [StoreProduct] { try await withCheckedThrowingContinuation { continuation in self.fetch(productIDs: productIDs) { result in continuation.resume(with: result) @@ -174,7 +178,7 @@ final class IAPProvider: IIAPProvider { } } - func addTransactionObserver(fallbackHandler: Closure>?) { + func addTransactionObserver(fallbackHandler: Closure>?) { purchaseProvider.addTransactionObserver(fallbackHandler: fallbackHandler) } @@ -188,6 +192,11 @@ final class IAPProvider: IIAPProvider { return try await eligibilityProvider.checkEligibility(products: products) } + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws { + try await purchaseProvider.restore() + } + #if os(iOS) || VISION_OS @available(iOS 15.0, *) @available(macOS, unavailable) diff --git a/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift b/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift index 100917471..318a1435f 100644 --- a/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift +++ b/Sources/Flare/Classes/Providers/IAPProvider/IIAPProvider.swift @@ -17,7 +17,7 @@ public protocol IIAPProvider { /// - Parameters: /// - productIDs: The list of product identifiers for which you wish to retrieve descriptions. /// - completion: The completion containing the response of retrieving products. - func fetch(productIDs: Set, completion: @escaping Closure>) + func fetch(productIDs: some Collection, completion: @escaping Closure>) /// Retrieves localized information from the App Store about a specified list of products. /// @@ -26,7 +26,7 @@ public protocol IIAPProvider { /// - Throws: `IAPError(error:)` if the request did fail with error. /// /// - Returns: An array of products. - func fetch(productIDs: Set) async throws -> [StoreProduct] + func fetch(productIDs: some Collection) async throws -> [StoreProduct] /// Performs a purchase of a product. /// @@ -134,7 +134,7 @@ public protocol IIAPProvider { /// The transactions array will only be synchronized with the server while the queue has observers. /// /// - Note: This may require that the user authenticate. - func addTransactionObserver(fallbackHandler: Closure>?) + func addTransactionObserver(fallbackHandler: Closure>?) /// Removes transaction observer from the payment queue. /// The transactions array will only be synchronized with the server while the queue has observers. @@ -150,6 +150,9 @@ public protocol IIAPProvider { @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) func checkEligibility(productIDs: Set) async throws -> [String: SubscriptionEligibility] + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws + #if os(iOS) || VISION_OS /// Present the refund request sheet for the specified transaction in a window scene. /// diff --git a/Sources/Flare/Classes/Providers/ProductProvider/Decorators/ProductsCacheProviderDecorator/CachingProductsProviderDecorator.swift b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/CachingProductsProviderDecorator/CachingProductsProviderDecorator.swift similarity index 87% rename from Sources/Flare/Classes/Providers/ProductProvider/Decorators/ProductsCacheProviderDecorator/CachingProductsProviderDecorator.swift rename to Sources/Flare/Classes/Providers/ProductProvider/Decorators/CachingProductsProviderDecorator/CachingProductsProviderDecorator.swift index d2a865962..6f993172a 100644 --- a/Sources/Flare/Classes/Providers/ProductProvider/Decorators/ProductsCacheProviderDecorator/CachingProductsProviderDecorator.swift +++ b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/CachingProductsProviderDecorator/CachingProductsProviderDecorator.swift @@ -46,7 +46,7 @@ final class CachingProductsProviderDecorator { /// - Parameter ids: The set of product IDs to retrieve cached products for. /// /// - Returns: A dictionary containing cached products for the specified IDs. - private func cachedProducts(ids: Set) -> [String: StoreProduct] { + private func cachedProducts(ids: some Collection) -> [String: StoreProduct] { let cachedProducts = _cache.wrappedValue.filter { ids.contains($0.key) } return cachedProducts } @@ -58,12 +58,12 @@ final class CachingProductsProviderDecorator { /// - fetcher: A closure to fetch missing products from the product provider. /// - completion: A closure to be called with the fetched products or an error. private func fetch( - productIDs: Set, - fetcher: (Set, @escaping (Result<[StoreProduct], IAPError>) -> Void) -> Void, + productIDs: some Collection, + fetcher: (any Collection, @escaping (Result<[StoreProduct], IAPError>) -> Void) -> Void, completion: @escaping ProductsHandler ) { let cachedProducts = cachedProducts(ids: productIDs) - let missingProducts = productIDs.subtracting(cachedProducts.keys) + let missingProducts = Set(productIDs).subtracting(cachedProducts.keys) if missingProducts.isEmpty { completion(.success(Array(cachedProducts.values))) @@ -89,8 +89,8 @@ final class CachingProductsProviderDecorator { /// - completion: A closure to be called with the fetched products or an error. private func fetch( fetchPolicy: FetchCachePolicy, - productIDs: Set, - fetcher: (Set, @escaping (Result<[StoreProduct], IAPError>) -> Void) -> Void, + productIDs: some Collection, + fetcher: (any Collection, @escaping (Result<[StoreProduct], IAPError>) -> Void) -> Void, completion: @escaping ProductsHandler ) { switch fetchPolicy { @@ -107,7 +107,7 @@ final class CachingProductsProviderDecorator { /// - productIDs: The set of product IDs to check the cache for. /// - completion: A closure to be called with the fetched products or an error. @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) - private func fetchSK2Products(productIDs: Set, completion: @escaping ProductsHandler) { + private func fetchSK2Products(productIDs: some Collection, completion: @escaping ProductsHandler) { AsyncHandler.call( completion: { result in switch result { @@ -127,7 +127,7 @@ final class CachingProductsProviderDecorator { // MARK: ICachingProductsProviderDecorator extension CachingProductsProviderDecorator: ICachingProductsProviderDecorator { - func fetch(productIDs: Set, requestID: String, completion: @escaping ProductsHandler) { + func fetch(productIDs: some Collection, requestID: String, completion: @escaping ProductsHandler) { fetch( fetchPolicy: configurationProvider.fetchCachePolicy, productIDs: productIDs, @@ -138,7 +138,7 @@ extension CachingProductsProviderDecorator: ICachingProductsProviderDecorator { } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) - func fetch(productIDs: Set) async throws -> [StoreProduct] { + func fetch(productIDs: some Collection) async throws -> [StoreProduct] { try await withCheckedThrowingContinuation { [weak self] continuation in guard let self = self else { continuation.resume(throwing: IAPError.unknown) diff --git a/Sources/Flare/Classes/Providers/ProductProvider/Decorators/ProductsCacheProviderDecorator/ICachingProductsProviderDecorator.swift b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/CachingProductsProviderDecorator/ICachingProductsProviderDecorator.swift similarity index 100% rename from Sources/Flare/Classes/Providers/ProductProvider/Decorators/ProductsCacheProviderDecorator/ICachingProductsProviderDecorator.swift rename to Sources/Flare/Classes/Providers/ProductProvider/Decorators/CachingProductsProviderDecorator/ICachingProductsProviderDecorator.swift diff --git a/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/ISortingProductsProviderDecorator.swift b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/ISortingProductsProviderDecorator.swift new file mode 100644 index 000000000..db3d7042e --- /dev/null +++ b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/ISortingProductsProviderDecorator.swift @@ -0,0 +1,8 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol ISortingProductsProviderDecorator: IProductProvider {} diff --git a/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/SortingProductsProviderDecorator.swift b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/SortingProductsProviderDecorator.swift new file mode 100644 index 000000000..b687790b8 --- /dev/null +++ b/Sources/Flare/Classes/Providers/ProductProvider/Decorators/SortingProductsProviderDecorator/SortingProductsProviderDecorator.swift @@ -0,0 +1,72 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - SortingProductsProviderDecorator + +final class SortingProductsProviderDecorator { + // MARK: Properties + + private let productProvider: IProductProvider + + // MARK: Initialization + + init(productProvider: IProductProvider) { + self.productProvider = productProvider + } + + // MARK: Private + + private func sort(productIDs: some Collection, products: [StoreProduct]) -> [StoreProduct] { + var sortedProducts: [StoreProduct] = [] + var set = Set(productIDs) + + for productID in productIDs { + if set.contains(productID), let product = products.by(id: productID) { + sortedProducts.append(product) + set.remove(productID) + } + } + + return sortedProducts + } +} + +// MARK: ISortingProductsProviderDecorator + +extension SortingProductsProviderDecorator: ISortingProductsProviderDecorator { + func fetch( + productIDs: some Collection, + requestID: String, + completion: @escaping ProductsHandler + ) { + productProvider.fetch(productIDs: productIDs, requestID: requestID) { [weak self] result in + guard let self = self else { return } + + switch result { + case let .success(products): + let sortedProducts = self.sort(productIDs: productIDs, products: products) + completion(.success(sortedProducts)) + case let .failure(error): + completion(.failure(error)) + } + } + } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func fetch(productIDs: some Collection) async throws -> [StoreProduct] { + let products = try await productProvider.fetch(productIDs: productIDs) + return sort(productIDs: productIDs, products: products) + } +} + +// MARK: Private + +private extension Array where Element: StoreProduct { + func by(id: String) -> StoreProduct? { + first(where: { $0.productIdentifier == id }) + } +} diff --git a/Sources/Flare/Classes/Providers/ProductProvider/IProductProvider.swift b/Sources/Flare/Classes/Providers/ProductProvider/IProductProvider.swift index 1e82f3158..55955ecf4 100644 --- a/Sources/Flare/Classes/Providers/ProductProvider/IProductProvider.swift +++ b/Sources/Flare/Classes/Providers/ProductProvider/IProductProvider.swift @@ -22,7 +22,7 @@ protocol IProductProvider { /// - productIDs: The list of product identifiers for which you wish to retrieve descriptions. /// - requestID: The request identifier. /// - completion: The completion containing the response of retrieving products. - func fetch(productIDs: Set, requestID: String, completion: @escaping ProductsHandler) + func fetch(productIDs: some Collection, requestID: String, completion: @escaping ProductsHandler) /// Retrieves localized information from the App Store about a specified list of products. /// @@ -32,5 +32,5 @@ protocol IProductProvider { /// /// - Returns: The requested products. @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) - func fetch(productIDs: Set) async throws -> [StoreProduct] + func fetch(productIDs: some Collection) async throws -> [StoreProduct] } diff --git a/Sources/Flare/Classes/Providers/ProductProvider/ProductProvider.swift b/Sources/Flare/Classes/Providers/ProductProvider/ProductProvider.swift index 57a6ad833..357229817 100644 --- a/Sources/Flare/Classes/Providers/ProductProvider/ProductProvider.swift +++ b/Sources/Flare/Classes/Providers/ProductProvider/ProductProvider.swift @@ -38,13 +38,13 @@ final class ProductProvider: NSObject, IProductProvider { // MARK: Internal - func fetch(productIDs ids: Set, requestID: String, completion: @escaping ProductsHandler) { + func fetch(productIDs ids: some Collection, requestID: String, completion: @escaping ProductsHandler) { let request = makeRequest(ids: ids, requestID: requestID) fetch(request: request, completion: completion) } @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) - func fetch(productIDs ids: Set) async throws -> [StoreProduct] { + func fetch(productIDs ids: some Collection) async throws -> [StoreProduct] { try await StoreKit.Product.products(for: ids).map { StoreProduct(product: $0) } } @@ -64,8 +64,8 @@ final class ProductProvider: NSObject, IProductProvider { /// - ids: The set of product IDs to include in the request. /// - requestID: The identifier for the request. /// - Returns: An instance of `SKProductsRequest`. - private func makeRequest(ids: Set, requestID: String) -> SKProductsRequest { - let request = SKProductsRequest(productIdentifiers: ids) + private func makeRequest(ids: some Collection, requestID: String) -> SKProductsRequest { + let request = SKProductsRequest(productIdentifiers: Set(ids)) request.id = requestID request.delegate = self return request diff --git a/Sources/Flare/Classes/Providers/PurchaseProvider/IPurchaseProvider.swift b/Sources/Flare/Classes/Providers/PurchaseProvider/IPurchaseProvider.swift index 2fb931923..cd290ad09 100644 --- a/Sources/Flare/Classes/Providers/PurchaseProvider/IPurchaseProvider.swift +++ b/Sources/Flare/Classes/Providers/PurchaseProvider/IPurchaseProvider.swift @@ -23,7 +23,7 @@ protocol IPurchaseProvider { /// The transactions array will only be synchronized with the server while the queue has observers. /// /// - Note: This may require that the user authenticate. - func addTransactionObserver(fallbackHandler: Closure>?) + func addTransactionObserver(fallbackHandler: Closure>?) /// Removes transaction observer from the payment queue. /// The transactions array will only be synchronized with the server while the queue has observers. @@ -57,6 +57,9 @@ protocol IPurchaseProvider { promotionalOffer: PromotionalOffer?, completion: @escaping PurchaseCompletionHandler ) + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws } extension IPurchaseProvider { diff --git a/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift b/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift index 7fc89aae7..479ffcd84 100644 --- a/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift +++ b/Sources/Flare/Classes/Providers/PurchaseProvider/PurchaseProvider.swift @@ -6,6 +6,8 @@ import Foundation import StoreKit +typealias FallbackHandler = Closure> + // MARK: - PurchaseProvider final class PurchaseProvider { @@ -14,9 +16,11 @@ final class PurchaseProvider { /// The provider is responsible for making in-app payments. private let paymentProvider: IPaymentProvider /// The transaction listener. - private let transactionListener: ITransactionListener? + private var transactionListener: ITransactionListener? /// The configuration provider. private let configurationProvider: IConfigurationProvider + /// The fallback handler. + private var fallbackHandler: FallbackHandler? // MARK: Initialization @@ -37,7 +41,7 @@ final class PurchaseProvider { if let transactionListener = transactionListener { self.transactionListener = transactionListener } else if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { - self.transactionListener = TransactionListener(updates: StoreKit.Transaction.updates) + self.configureTransactionListener() } else { self.transactionListener = nil } @@ -90,7 +94,7 @@ final class PurchaseProvider { if let error = error as? IAPError { await completion(.failure(error)) } else { - await completion(.failure(IAPError.with(error: error))) + await completion(.failure(.with(error: error))) } } case let .failure(error): @@ -116,6 +120,15 @@ final class PurchaseProvider { } } + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + private func configureTransactionListener() { + self.transactionListener = TransactionListener(delegate: self, updates: StoreKit.Transaction.updates) + + Task { + await self.transactionListener?.listenForTransaction() + } + } + private func log(error: Error, productID: String) { Logger.error(message: L10n.Purchase.productPurchaseFailed(productID, error.localizedDescription)) } @@ -199,11 +212,13 @@ extension PurchaseProvider: IPurchaseProvider { } } - func addTransactionObserver(fallbackHandler: Closure>?) { + func addTransactionObserver(fallbackHandler: FallbackHandler?) { + self.fallbackHandler = fallbackHandler + paymentProvider.set { _, result in switch result { case let .success(transaction): - fallbackHandler?(.success(PaymentTransaction(transaction))) + fallbackHandler?(.success(StoreTransaction(paymentTransaction: PaymentTransaction(transaction)))) case let .failure(error): fallbackHandler?(.failure(error)) } @@ -214,4 +229,22 @@ extension PurchaseProvider: IPurchaseProvider { func removeTransactionObserver() { paymentProvider.removeTransactionObserver() } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws { + try await AppStore.sync() + } +} + +// MARK: TransactionListenerDelegate + +extension PurchaseProvider: TransactionListenerDelegate { + func transactionListener(_: ITransactionListener, transactionDidUpdate result: Result) { + switch result { + case let .success(transaction): + self.fallbackHandler?(.success(transaction)) + case let .failure(error): + self.fallbackHandler?(.failure(error)) + } + } } diff --git a/Sources/Flare/Makefile b/Sources/Flare/Makefile new file mode 100644 index 000000000..1a5286c90 --- /dev/null +++ b/Sources/Flare/Makefile @@ -0,0 +1,2 @@ +swiftgen: + swiftgen \ No newline at end of file diff --git a/swiftgen.yml b/Sources/Flare/swiftgen.yml similarity index 69% rename from swiftgen.yml rename to Sources/Flare/swiftgen.yml index d54965973..cb94fd344 100644 --- a/swiftgen.yml +++ b/Sources/Flare/swiftgen.yml @@ -1,5 +1,5 @@ -input_dir: Sources/Flare/Resources -output_dir: Sources/Flare/Classes/Generated +input_dir: Resources +output_dir: Classes/Generated strings: inputs: - Localizable.strings diff --git a/Sources/FlareMock/Fakes/StoreProduct+Fake.swift b/Sources/FlareMock/Fakes/StoreProduct+Fake.swift new file mode 100644 index 000000000..5e0d9be54 --- /dev/null +++ b/Sources/FlareMock/Fakes/StoreProduct+Fake.swift @@ -0,0 +1,39 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import Flare +import StoreKit + +public extension StoreProduct { + static func fake( + localizedTitle: String? = "My App Lifetime", + localizedDescription: String? = "Lifetime access to additional content", + price: Decimal? = 1.0, + currencyCode: String? = "USD", + localizedPriceString: String? = "$19.99", + productIdentifier: String? = "com.flare.app.lifetime", + productType: ProductType? = nil, + productCategory: ProductCategory? = nil, + subscriptionPeriod: SubscriptionPeriod? = nil, + introductoryDiscount: StoreProductDiscount? = nil, + discounts: [StoreProductDiscount] = [], + subscriptionGroupIdentifier: String? = nil + ) -> StoreProduct { + let mock = ProductMock() + mock.stubbedLocalizedTitle = localizedTitle + mock.stubbedLocalizedDescription = localizedDescription + mock.stubbedPrice = price + mock.stubbedCurrencyCode = currencyCode + mock.stubbedLocalizedPriceString = localizedPriceString + mock.stubbedProductIdentifier = productIdentifier + mock.stubbedProductType = productType + mock.stubbedProductCategory = productCategory + mock.stubbedSubscriptionPeriod = subscriptionPeriod + mock.stubbedIntroductoryDiscount = introductoryDiscount + mock.stubbedDiscounts = discounts + mock.stubbedSubscriptionGroupIdentifier = subscriptionGroupIdentifier + return StoreProduct(mock) + } +} diff --git a/Sources/FlareMock/Fakes/StoreTransaction+Fake.swift b/Sources/FlareMock/Fakes/StoreTransaction+Fake.swift new file mode 100644 index 000000000..6db725f7f --- /dev/null +++ b/Sources/FlareMock/Fakes/StoreTransaction+Fake.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import Flare +import Foundation + +public extension StoreTransaction { + static func fake() -> StoreTransaction { + StoreTransaction(paymentTransaction: PaymentTransaction(PaymentTransactionMock())) + } +} diff --git a/Sources/FlareMock/Mocks/PaymentTransactionMock.swift b/Sources/FlareMock/Mocks/PaymentTransactionMock.swift new file mode 100644 index 000000000..9df171c64 --- /dev/null +++ b/Sources/FlareMock/Mocks/PaymentTransactionMock.swift @@ -0,0 +1,48 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +public final class PaymentTransactionMock: SKPaymentTransaction { + override public init() {} + + public var invokedTransactionState = false + public var invokedTransactionStateCount = 0 + public var stubbedTransactionState: SKPaymentTransactionState! + + override public var transactionState: SKPaymentTransactionState { + stubbedTransactionState + } + + public var invokedTransactionIndentifier = false + public var invokedTransactionIndentifierCount = 0 + public var stubbedTransactionIndentifier: String? + + override public var transactionIdentifier: String? { + invokedTransactionIndentifier = true + invokedTransactionStateCount += 1 + return stubbedTransactionIndentifier + } + + public var invokedPayment = false + public var invokedPaymentCount = 0 + public var stubbedPayment: SKPayment! + + override public var payment: SKPayment { + invokedPayment = true + invokedPaymentCount += 1 + return stubbedPayment + } + + public var stubbedOriginal: SKPaymentTransaction? + override public var original: SKPaymentTransaction? { + stubbedOriginal + } + + public var stubbedError: Error? + override public var error: Error? { + stubbedError + } +} diff --git a/Sources/FlareMock/Mocks/ProductMock.swift b/Sources/FlareMock/Mocks/ProductMock.swift new file mode 100644 index 000000000..18db19af9 --- /dev/null +++ b/Sources/FlareMock/Mocks/ProductMock.swift @@ -0,0 +1,138 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import Flare +import StoreKit + +public final class ProductMock: ISKProduct { + public init() {} + + public var invokedLocalizedDescriptionGetter = false + public var invokedLocalizedDescriptionGetterCount = 0 + public var stubbedLocalizedDescription: String! = "" + + public var localizedDescription: String { + invokedLocalizedDescriptionGetter = true + invokedLocalizedDescriptionGetterCount += 1 + return stubbedLocalizedDescription + } + + public var invokedLocalizedTitleGetter = false + public var invokedLocalizedTitleGetterCount = 0 + public var stubbedLocalizedTitle: String! = "" + + public var localizedTitle: String { + invokedLocalizedTitleGetter = true + invokedLocalizedTitleGetterCount += 1 + return stubbedLocalizedTitle + } + + public var invokedCurrencyCodeGetter = false + public var invokedCurrencyCodeGetterCount = 0 + public var stubbedCurrencyCode: String! + + public var currencyCode: String? { + invokedCurrencyCodeGetter = true + invokedCurrencyCodeGetterCount += 1 + return stubbedCurrencyCode + } + + public var invokedPriceGetter = false + public var invokedPriceGetterCount = 0 + public var stubbedPrice: Decimal! + + public var price: Decimal { + invokedPriceGetter = true + invokedPriceGetterCount += 1 + return stubbedPrice + } + + public var invokedLocalizedPriceStringGetter = false + public var invokedLocalizedPriceStringGetterCount = 0 + public var stubbedLocalizedPriceString: String! + + public var localizedPriceString: String? { + invokedLocalizedPriceStringGetter = true + invokedLocalizedPriceStringGetterCount += 1 + return stubbedLocalizedPriceString + } + + public var invokedProductIdentifierGetter = false + public var invokedProductIdentifierGetterCount = 0 + public var stubbedProductIdentifier: String! = "" + + public var productIdentifier: String { + invokedProductIdentifierGetter = true + invokedProductIdentifierGetterCount += 1 + return stubbedProductIdentifier + } + + public var invokedProductTypeGetter = false + public var invokedProductTypeGetterCount = 0 + public var stubbedProductType: ProductType! + + public var productType: ProductType? { + invokedProductTypeGetter = true + invokedProductTypeGetterCount += 1 + return stubbedProductType + } + + public var invokedProductCategoryGetter = false + public var invokedProductCategoryGetterCount = 0 + public var stubbedProductCategory: ProductCategory! + + public var productCategory: ProductCategory? { + invokedProductCategoryGetter = true + invokedProductCategoryGetterCount += 1 + return stubbedProductCategory + } + + public var invokedSubscriptionPeriodGetter = false + public var invokedSubscriptionPeriodGetterCount = 0 + public var stubbedSubscriptionPeriod: SubscriptionPeriod! + + public var subscriptionPeriod: SubscriptionPeriod? { + invokedSubscriptionPeriodGetter = true + invokedSubscriptionPeriodGetterCount += 1 + return stubbedSubscriptionPeriod + } + + public var invokedIntroductoryDiscountGetter = false + public var invokedIntroductoryDiscountGetterCount = 0 + public var stubbedIntroductoryDiscount: StoreProductDiscount! + + public var introductoryDiscount: StoreProductDiscount? { + invokedIntroductoryDiscountGetter = true + invokedIntroductoryDiscountGetterCount += 1 + return stubbedIntroductoryDiscount + } + + public var invokedDiscountsGetter = false + public var invokedDiscountsGetterCount = 0 + public var stubbedDiscounts: [StoreProductDiscount]! = [] + + public var discounts: [StoreProductDiscount] { + invokedDiscountsGetter = true + invokedDiscountsGetterCount += 1 + return stubbedDiscounts + } + + // swiftlint:disable identifier_name + public var invokedSubscriptionGroupIdentifierGetter = false + public var invokedSubscriptionGroupIdentifierGetterCount = 0 + public var stubbedSubscriptionGroupIdentifier: String! + + public var subscriptionGroupIdentifier: String? { + invokedSubscriptionGroupIdentifierGetter = true + invokedSubscriptionGroupIdentifierGetterCount += 1 + return stubbedSubscriptionGroupIdentifier + } + + // swiftlint:enable identifier_name + + public var subscription: SubscriptionInfo? { + nil + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/AnyProductStyle.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/AnyProductStyle.swift new file mode 100644 index 000000000..75a9fb081 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/AnyProductStyle.swift @@ -0,0 +1,31 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct AnyProductStyle: IProductStyle { + // MARK: Properties + + /// A private property to hold the closure that creates the body of the view + private var _makeBody: (Configuration) -> AnyView + + // MARK: Initialization + + /// Initializes the `AnyProductStyle` with a specific style conforming to `IProductStyle`. + /// + /// - Parameter style: A product style. + init(style: S) { + _makeBody = { configuration in + AnyView(style.makeBody(configuration: configuration)) + } + } + + // MARK: IProductStyle + + /// Implements the makeBody method required by `IProductStyle`. + func makeBody(configuration: Configuration) -> some View { + _makeBody(configuration) + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/AnySubscriptionControlStyle.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/AnySubscriptionControlStyle.swift new file mode 100644 index 000000000..f12f252ef --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/AnySubscriptionControlStyle.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct AnySubscriptionControlStyle: ISubscriptionControlStyle { + // MARK: Properties + + let style: any ISubscriptionControlStyle + + /// A private property to hold the closure that creates the body of the view + private var _makeBody: (Configuration) -> AnyView + + // MARK: Initialization + + /// Initializes the `AnyProductStyle` with a specific style conforming to `IProductStyle`. + /// + /// - Parameter style: A product style. + init(style: S) { + self.style = style + _makeBody = { configuration in + AnyView(style.makeBody(configuration: configuration)) + } + } + + // MARK: IProductStyle + + /// Implements the makeBody method required by `IProductStyle`. + func makeBody(configuration: Configuration) -> some View { + _makeBody(configuration) + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/ProductAssemblyKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/ProductAssemblyKey.swift new file mode 100644 index 000000000..332829c4a --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/ProductAssemblyKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProductAssemblyKey + +private struct ProductAssemblyKey: EnvironmentKey { + static var defaultValue: IProductViewAssembly? +} + +extension EnvironmentValues { + var productViewAssembly: IProductViewAssembly? { + get { self[ProductAssemblyKey.self] } + set { self[ProductAssemblyKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/StoreButtonsAssemblyKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/StoreButtonsAssemblyKey.swift new file mode 100644 index 000000000..63bd28d7f --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/Assemblies/StoreButtonsAssemblyKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - StoreButtonsAssemblyKey + +private struct StoreButtonsAssemblyKey: EnvironmentKey { + static var defaultValue: IStoreButtonsAssembly? +} + +extension EnvironmentValues { + var storeButtonsAssembly: IStoreButtonsAssembly? { + get { self[StoreButtonsAssemblyKey.self] } + set { self[StoreButtonsAssemblyKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/BlurEffectStyleKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/BlurEffectStyleKey.swift new file mode 100644 index 000000000..5e96dd9c0 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/BlurEffectStyleKey.swift @@ -0,0 +1,21 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - BlurEffectStyleKey + +#if os(iOS) || os(tvOS) + private struct BlurEffectStyleKey: EnvironmentKey { + static var defaultValue: UIBlurEffect.Style = .light + } + + extension EnvironmentValues { + var blurEffectStyle: UIBlurEffect.Style { + get { self[BlurEffectStyleKey.self] } + set { self[BlurEffectStyleKey.self] = newValue } + } + } +#endif diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/PoliciesButtonStyleKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/PoliciesButtonStyleKey.swift new file mode 100644 index 000000000..ecdca5c55 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/PoliciesButtonStyleKey.swift @@ -0,0 +1,21 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PoliciesButtonStyleKey + +@available(watchOS, unavailable) +private struct PoliciesButtonStyleKey: EnvironmentKey { + static var defaultValue: AnyPoliciesButtonStyle = .init(style: AutomaticPoliciesButtonStyle()) +} + +@available(watchOS, unavailable) +extension EnvironmentValues { + var policiesButtonStyle: AnyPoliciesButtonStyle { + get { self[PoliciesButtonStyleKey.self] } + set { self[PoliciesButtonStyleKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/ProductStyleKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/ProductStyleKey.swift new file mode 100644 index 000000000..b466c0858 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/ProductStyleKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProductStyleKey + +private struct ProductStyleKey: EnvironmentKey { + static var defaultValue = AnyProductStyle(style: CompactProductStyle()) +} + +extension EnvironmentValues { + var productViewStyle: AnyProductStyle { + get { self[ProductStyleKey.self] } + set { self[ProductStyleKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseCompletionKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseCompletionKey.swift new file mode 100644 index 000000000..617e2bb74 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseCompletionKey.swift @@ -0,0 +1,22 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +public typealias PurchaseCompletionHandler = (StoreProduct, Result) -> Void + +// MARK: - PurchaseCompletionKey + +private struct PurchaseCompletionKey: EnvironmentKey { + static var defaultValue: PurchaseCompletionHandler? +} + +extension EnvironmentValues { + var purchaseCompletion: PurchaseCompletionHandler? { + get { self[PurchaseCompletionKey.self] } + set { self[PurchaseCompletionKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseOptionKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseOptionKey.swift new file mode 100644 index 000000000..44d2f7a36 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/PurchaseOptionKey.swift @@ -0,0 +1,23 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import StoreKit +import SwiftUI + +typealias PurchaseOptionHandler = (StoreProduct) -> PurchaseOptions + +// MARK: - PurchaseOptionKey + +private struct PurchaseOptionKey: EnvironmentKey { + static var defaultValue: PurchaseOptionHandler? +} + +extension EnvironmentValues { + var purchaseOptions: PurchaseOptionHandler? { + get { self[PurchaseOptionKey.self] } + set { self[PurchaseOptionKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonKey.swift new file mode 100644 index 000000000..071693cb6 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - StoreButtonKey + +private struct StoreButtonKey: EnvironmentKey { + static var defaultValue: [StoreButtonType] = [] +} + +extension EnvironmentValues { + var storeButton: [StoreButtonType] { + get { self[StoreButtonKey.self] } + set { self[StoreButtonKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonViewFontWeightKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonViewFontWeightKey.swift new file mode 100644 index 000000000..a351eaba6 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/StoreButtonViewFontWeightKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - StoreButtonViewFontWeightKey + +private struct StoreButtonViewFontWeightKey: EnvironmentKey { + static var defaultValue: Font.Weight = .regular +} + +extension EnvironmentValues { + var storeButtonViewFontWeight: Font.Weight { + get { self[StoreButtonViewFontWeightKey.self] } + set { self[StoreButtonViewFontWeightKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionBackgroundKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionBackgroundKey.swift new file mode 100644 index 000000000..c48ccfe0b --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionBackgroundKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionBackgroundKey + +private struct SubscriptionBackgroundKey: EnvironmentKey { + static var defaultValue: Color = Palette.systemBackground +} + +extension EnvironmentValues { + var subscriptionBackground: Color { + get { self[SubscriptionBackgroundKey.self] } + set { self[SubscriptionBackgroundKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionControlStyleKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionControlStyleKey.swift new file mode 100644 index 000000000..01df8f43b --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionControlStyleKey.swift @@ -0,0 +1,21 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionControlStyleKey + +@available(watchOS, unavailable) +private struct SubscriptionControlStyleKey: EnvironmentKey { + static var defaultValue: AnySubscriptionControlStyle = .init(style: .automatic) +} + +@available(watchOS, unavailable) +extension EnvironmentValues { + var subscriptionControlStyle: AnySubscriptionControlStyle { + get { self[SubscriptionControlStyleKey.self] } + set { self[SubscriptionControlStyleKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionHeaderContentBackgroundKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionHeaderContentBackgroundKey.swift new file mode 100644 index 000000000..3ba5ccfea --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionHeaderContentBackgroundKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionHeaderContentBackgroundKey + +private struct SubscriptionHeaderContentBackgroundKey: EnvironmentKey { + static var defaultValue: Color = .clear +} + +extension EnvironmentValues { + var subscriptionHeaderContentBackground: Color { + get { self[SubscriptionHeaderContentBackgroundKey.self] } + set { self[SubscriptionHeaderContentBackgroundKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionMarketingContentKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionMarketingContentKey.swift new file mode 100644 index 000000000..f7169052f --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionMarketingContentKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionMarketingContentKey + +private struct SubscriptionMarketingContentKey: EnvironmentKey { + static var defaultValue: AnyView? +} + +extension EnvironmentValues { + var subscriptionMarketingContent: AnyView? { + get { self[SubscriptionMarketingContentKey.self] } + set { self[SubscriptionMarketingContentKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPickerItemBackgroundKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPickerItemBackgroundKey.swift new file mode 100644 index 000000000..a2ea62a28 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPickerItemBackgroundKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionPickerItemBackgroundKey + +private struct SubscriptionPickerItemBackgroundKey: EnvironmentKey { + static var defaultValue: Color = Palette.systemGray5 +} + +extension EnvironmentValues { + var subscriptionPickerItemBackground: Color { + get { self[SubscriptionPickerItemBackgroundKey.self] } + set { self[SubscriptionPickerItemBackgroundKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyDestinationKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyDestinationKey.swift new file mode 100644 index 000000000..0d3adf7ab --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyDestinationKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionPrivacyPolicyDestinationKey + +private struct SubscriptionPrivacyPolicyDestinationKey: EnvironmentKey { + static var defaultValue: AnyView? +} + +extension EnvironmentValues { + var subscriptionPrivacyPolicyDestination: AnyView? { + get { self[SubscriptionPrivacyPolicyDestinationKey.self] } + set { self[SubscriptionPrivacyPolicyDestinationKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyURLKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyURLKey.swift new file mode 100644 index 000000000..08d40b901 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionPrivacyPolicyURLKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionPrivacyPolicyURLKey + +private struct SubscriptionPrivacyPolicyURLKey: EnvironmentKey { + static var defaultValue: URL? +} + +extension EnvironmentValues { + var subscriptionPrivacyPolicyURL: URL? { + get { self[SubscriptionPrivacyPolicyURLKey.self] } + set { self[SubscriptionPrivacyPolicyURLKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionStoreButtonLabelKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionStoreButtonLabelKey.swift new file mode 100644 index 000000000..4b7019641 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionStoreButtonLabelKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionStoreButtonLabelKey + +private struct SubscriptionStoreButtonLabelKey: EnvironmentKey { + static var defaultValue: SubscriptionStoreButtonLabel = .action +} + +extension EnvironmentValues { + var subscriptionStoreButtonLabel: SubscriptionStoreButtonLabel { + get { self[SubscriptionStoreButtonLabelKey.self] } + set { self[SubscriptionStoreButtonLabelKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceDestinationKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceDestinationKey.swift new file mode 100644 index 000000000..a581e3647 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceDestinationKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionTermsOfServiceDestinationKey + +private struct SubscriptionTermsOfServiceDestinationKey: EnvironmentKey { + static var defaultValue: AnyView? +} + +extension EnvironmentValues { + var subscriptionTermsOfServiceDestination: AnyView? { + get { self[SubscriptionTermsOfServiceDestinationKey.self] } + set { self[SubscriptionTermsOfServiceDestinationKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceURLKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceURLKey.swift new file mode 100644 index 000000000..af1d57532 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionTermsOfServiceURLKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionTermsOfServiceURLKey + +private struct SubscriptionTermsOfServiceURLKey: EnvironmentKey { + static var defaultValue: URL? +} + +extension EnvironmentValues { + var subscriptionTermsOfServiceURL: URL? { + get { self[SubscriptionTermsOfServiceURLKey.self] } + set { self[SubscriptionTermsOfServiceURLKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionViewTintKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionViewTintKey.swift new file mode 100644 index 000000000..ff2f8b2ab --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionViewTintKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionViewTintKey + +private struct SubscriptionViewTintKey: EnvironmentKey { + static var defaultValue: Color = .blue +} + +extension EnvironmentValues { + var subscriptionViewTint: Color { + get { self[SubscriptionViewTintKey.self] } + set { self[SubscriptionViewTintKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionsWrapperViewStyleKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionsWrapperViewStyleKey.swift new file mode 100644 index 000000000..65d284e5d --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/SubscriptionsWrapperViewStyleKey.swift @@ -0,0 +1,21 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionsWrapperViewStyleKey + +@available(watchOS, unavailable) +private struct SubscriptionsWrapperViewStyleKey: EnvironmentKey { + static var defaultValue = AnySubscriptionsWrapperViewStyle(style: AutomaticSubscriptionsWrapperViewStyle()) +} + +@available(watchOS, unavailable) +extension EnvironmentValues { + var subscriptionsWrapperViewStyle: AnySubscriptionsWrapperViewStyle { + get { self[SubscriptionsWrapperViewStyleKey.self] } + set { self[SubscriptionsWrapperViewStyleKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/EnvironmentKey/TintColorKey.swift b/Sources/FlareUI/Classes/Core/EnvironmentKey/TintColorKey.swift new file mode 100644 index 000000000..341a0e0dd --- /dev/null +++ b/Sources/FlareUI/Classes/Core/EnvironmentKey/TintColorKey.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - TintColorKey + +private struct TintColorKey: EnvironmentKey { + static var defaultValue: Color = .blue +} + +extension EnvironmentValues { + var tintColor: Color { + get { self[TintColorKey.self] } + set { self[TintColorKey.self] = newValue } + } +} diff --git a/Sources/FlareUI/Classes/Core/Extensions/Array+RemoveDuplicates.swift b/Sources/FlareUI/Classes/Core/Extensions/Array+RemoveDuplicates.swift new file mode 100644 index 000000000..db8c8ad86 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Extensions/Array+RemoveDuplicates.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +extension Array where Element: Hashable { + func removingDuplicates() -> [Element] { + var set = Set() + return filter { set.insert($0).inserted } + } +} diff --git a/Sources/FlareUI/Classes/Core/Extensions/String+SubSequence.swift b/Sources/FlareUI/Classes/Core/Extensions/String+SubSequence.swift new file mode 100644 index 000000000..6a579abe0 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Extensions/String+SubSequence.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +extension String { + init?(_ substring: SubSequence?) { + guard let substring else { return nil } + self.init(substring) + } +} diff --git a/Sources/FlareUI/Classes/Core/Extensions/StringProtocol+Words.swift b/Sources/FlareUI/Classes/Core/Extensions/StringProtocol+Words.swift new file mode 100644 index 000000000..6b7040d0e --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Extensions/StringProtocol+Words.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +extension StringProtocol { + var words: [SubSequence] { + var byWords: [SubSequence] = [] + enumerateSubstrings(in: startIndex..., options: .byWords) { _, range, _, _ in + byWords.append(self[range]) + } + return byWords + } +} diff --git a/Sources/FlareUI/Classes/Core/Extensions/View+EraseToAnyView.swift b/Sources/FlareUI/Classes/Core/Extensions/View+EraseToAnyView.swift new file mode 100644 index 000000000..544f5e657 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Extensions/View+EraseToAnyView.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +extension View { + func eraseToAnyView() -> AnyView { + AnyView(self) + } +} diff --git a/Sources/FlareUI/Classes/Core/Formatters/DateComponentsFormatter+Full.swift b/Sources/FlareUI/Classes/Core/Formatters/DateComponentsFormatter+Full.swift new file mode 100644 index 000000000..c4429b527 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Formatters/DateComponentsFormatter+Full.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +extension DateComponentsFormatter { + static let full: IDateComponentsFormatter = { + let formatter = DateComponentsFormatter() + formatter.maximumUnitCount = 1 + formatter.unitsStyle = .full + formatter.zeroFormattingBehavior = .dropAll + return formatter + }() +} diff --git a/Sources/FlareUI/Classes/Core/Formatters/IDateComponentsFormatter.swift b/Sources/FlareUI/Classes/Core/Formatters/IDateComponentsFormatter.swift new file mode 100644 index 000000000..cf1438140 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Formatters/IDateComponentsFormatter.swift @@ -0,0 +1,18 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - IDateComponentsFormatter + +protocol IDateComponentsFormatter { + var allowedUnits: NSCalendar.Unit { get set } + + func string(from: DateComponents) -> String? +} + +// MARK: - DateComponentsFormatter + IDateComponentsFormatter + +extension DateComponentsFormatter: IDateComponentsFormatter {} diff --git a/Sources/FlareUI/Classes/Core/Helpers/Array+StoreProduct.swift b/Sources/FlareUI/Classes/Core/Helpers/Array+StoreProduct.swift new file mode 100644 index 000000000..c3931f980 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Helpers/Array+StoreProduct.swift @@ -0,0 +1,14 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +// MARK: - Extensions + +extension Array where Element: StoreProduct { + func by(id: String) -> StoreProduct? { + first(where: { $0.productIdentifier == id }) + } +} diff --git a/Sources/FlareUI/Classes/Core/Helpers/Color+UIColor.swift b/Sources/FlareUI/Classes/Core/Helpers/Color+UIColor.swift new file mode 100644 index 000000000..4441603fd --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Helpers/Color+UIColor.swift @@ -0,0 +1,36 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +#if os(iOS) || os(tvOS) + typealias UIColor = UIKit.UIColor +#elseif os(macOS) + typealias UIColor = NSColor +#endif + +// swiftlint:disable identifier_name +extension Color { + func uiColor() -> UIColor { + if #available(iOS 14.0, tvOS 14.0, macOS 11.0, *) { + return UIColor(self) + } + + let scanner = Scanner(string: description.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)) + var hexNumber: UInt64 = 0 + var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0, a: CGFloat = 0.0 + + let result = scanner.scanHexInt64(&hexNumber) + if result { + r = CGFloat((hexNumber & 0xFF00_0000) >> 24) / 255 + g = CGFloat((hexNumber & 0x00FF_0000) >> 16) / 255 + b = CGFloat((hexNumber & 0x0000_FF00) >> 8) / 255 + a = CGFloat(hexNumber & 0x0000_00FF) / 255 + } + return UIColor(red: r, green: g, blue: b, alpha: a) + } +} + +// swiftlint:enable identifier_name diff --git a/Sources/FlareUI/Classes/Core/Helpers/Error+IAP.swift b/Sources/FlareUI/Classes/Core/Helpers/Error+IAP.swift new file mode 100644 index 000000000..534b1355d --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Helpers/Error+IAP.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +extension Error { + var iap: IAPError { + if let error = self as? IAPError { + return error + } + return .with(error: self) + } +} diff --git a/Sources/FlareUI/Classes/Core/Helpers/Value.swift b/Sources/FlareUI/Classes/Core/Helpers/Value.swift new file mode 100644 index 000000000..78026fa2e --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Helpers/Value.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +func value(default: T, tvOS: T? = nil, macOS: T? = nil, iOS: T? = nil, watchOS: T? = nil) -> T { + #if os(iOS) + return iOS ?? `default` + #elseif os(macOS) + return macOS ?? `default` + #elseif os(tvOS) + return tvOS ?? `default` + #elseif os(watchOS) + return watchOS ?? `default` + #else + return `default` + #endif +} diff --git a/Sources/FlareUI/Classes/Core/Models/Internal/PriceDisplayFormat.swift b/Sources/FlareUI/Classes/Core/Models/Internal/PriceDisplayFormat.swift new file mode 100644 index 000000000..8bb30c808 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/Internal/PriceDisplayFormat.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - PriceDisplayFormat + +enum PriceDisplayFormat { + case short + case full +} diff --git a/Sources/FlareUI/Classes/Core/Models/Internal/ProductStyle.swift b/Sources/FlareUI/Classes/Core/Models/Internal/ProductStyle.swift new file mode 100644 index 000000000..aadb4162b --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/Internal/ProductStyle.swift @@ -0,0 +1,11 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +enum ProductStyle { + case compact + case large +} diff --git a/Sources/FlareUI/Classes/Core/Models/PaywallType.swift b/Sources/FlareUI/Classes/Core/Models/PaywallType.swift new file mode 100644 index 000000000..ddd6bc39a --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/PaywallType.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +/// An enum represents a paywall type. +public enum PaywallType { + /// Represents a paywall for subscriptions. + case subscriptions(type: any Collection) + + /// Represents a paywall for specific products identified by their IDs. + case products(productIDs: any Collection) +} diff --git a/Sources/FlareUI/Classes/Core/Models/PurchaseOptions.swift b/Sources/FlareUI/Classes/Core/Models/PurchaseOptions.swift new file mode 100644 index 000000000..134c4b38e --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/PurchaseOptions.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import StoreKit + +struct PurchaseOptions { + // MARK: Properties + + private var _options: Any? + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + var options: Set? { + _options as? Set + } + + // MARK: Initialization + + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + init(options: Set) { + self._options = options + } +} diff --git a/Sources/FlareUI/Classes/Core/Models/SubscptionType.swift b/Sources/FlareUI/Classes/Core/Models/SubscptionType.swift new file mode 100644 index 000000000..765ea9a4b --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/SubscptionType.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +/// An enum represents a subscription type. +public enum SubscptionType { + /// Represents a subscription type identified by a group ID. + case groupID(id: String) + + /// Represents a subscription type for multiple subscriptions identified by their IDs. + case subscriptions(ids: Set) +} diff --git a/Sources/FlareUI/Classes/Core/Models/SubscriptionStatusVerifierType.swift b/Sources/FlareUI/Classes/Core/Models/SubscriptionStatusVerifierType.swift new file mode 100644 index 000000000..6d28a56dc --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/SubscriptionStatusVerifierType.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public enum SubscriptionStatusVerifierType { + @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) + case automatic + + case custom(ISubscriptionStatusVerifier) +} diff --git a/Sources/FlareUI/Classes/Core/Models/SubscriptionStoreButtonLabel.swift b/Sources/FlareUI/Classes/Core/Models/SubscriptionStoreButtonLabel.swift new file mode 100644 index 000000000..e337b7812 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/SubscriptionStoreButtonLabel.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public enum SubscriptionStoreButtonLabel { + case multiline + case price + case action + case displayName +} diff --git a/Sources/FlareUI/Classes/Core/Models/UIConfiguration.swift b/Sources/FlareUI/Classes/Core/Models/UIConfiguration.swift new file mode 100644 index 000000000..4ef02c808 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Models/UIConfiguration.swift @@ -0,0 +1,18 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public struct UIConfiguration { + // MARK: Properties + + public let subscriptionVerifier: SubscriptionStatusVerifierType + + // MARK: Initialization + + public init(subscriptionVerifier: SubscriptionStatusVerifierType) { + self.subscriptionVerifier = subscriptionVerifier + } +} diff --git a/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/ConfigurationProvider.swift b/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/ConfigurationProvider.swift new file mode 100644 index 000000000..e57afb7bd --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/ConfigurationProvider.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - ConfigurationProvider + +final class ConfigurationProvider { + // MARK: Properties + + private var configuration: UIConfiguration? +} + +// MARK: IConfigurationProvider + +extension ConfigurationProvider: IConfigurationProvider { + var subscriptionVerifierType: SubscriptionStatusVerifierType? { + guard let configuration else { + if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { + return .automatic + } + return nil + } + return configuration.subscriptionVerifier + } + + func configure(with configuration: UIConfiguration) { + self.configuration = configuration + } +} diff --git a/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/IConfigurationProvider.swift b/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/IConfigurationProvider.swift new file mode 100644 index 000000000..3ac77739e --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/ConfigurationProvider/IConfigurationProvider.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol IConfigurationProvider { + var subscriptionVerifierType: SubscriptionStatusVerifierType? { get } + + func configure(with configuration: UIConfiguration) +} diff --git a/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/ISubscriptionStatusVerifierProvider.swift b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/ISubscriptionStatusVerifierProvider.swift new file mode 100644 index 000000000..6d7a67e78 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/ISubscriptionStatusVerifierProvider.swift @@ -0,0 +1,10 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol ISubscriptionStatusVerifierProvider { + var subscriptionStatusVerifier: ISubscriptionStatusVerifier? { get } +} diff --git a/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/SubscriptionStatusVerifierProvider.swift b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/SubscriptionStatusVerifierProvider.swift new file mode 100644 index 000000000..866b5aee2 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusProvider/SubscriptionStatusVerifierProvider.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - SubscriptionStatusVerifierProvider + +final class SubscriptionStatusVerifierProvider { + // MARK: Properties + + private let configurationProvider: IConfigurationProvider + private let subscriptionStatusVerifierResolver: ISubscriptionStatusVerifierTypeResolver + + // MARK: Initialization + + init( + configurationProvider: IConfigurationProvider, + subscriptionStatusVerifierResolver: ISubscriptionStatusVerifierTypeResolver + ) { + self.configurationProvider = configurationProvider + self.subscriptionStatusVerifierResolver = subscriptionStatusVerifierResolver + } +} + +// MARK: ISubscriptionStatusVerifierProvider + +extension SubscriptionStatusVerifierProvider: ISubscriptionStatusVerifierProvider { + var subscriptionStatusVerifier: (any ISubscriptionStatusVerifier)? { + guard let type = configurationProvider.subscriptionVerifierType else { return nil } + return subscriptionStatusVerifierResolver.resolve(type) + } +} diff --git a/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/ISubscriptionStatusVerifier.swift b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/ISubscriptionStatusVerifier.swift new file mode 100644 index 000000000..11fd1d171 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/ISubscriptionStatusVerifier.swift @@ -0,0 +1,10 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +public protocol ISubscriptionStatusVerifier { + func validate(_ storeProduct: StoreProduct) async throws -> Bool +} diff --git a/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/SubscriptionStatusVerifier.swift b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/SubscriptionStatusVerifier.swift new file mode 100644 index 000000000..c6ceed8f0 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Providers/SubscriptionStatusVerifier/SubscriptionStatusVerifier.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) +final class SubscriptionStatusVerifier: ISubscriptionStatusVerifier { + // MARK: IActiveSubscriptionProvider + + func validate(_ storeProduct: StoreProduct) async throws -> Bool { + guard let subscription = storeProduct.subscription else { return false } + + let statuses = try await subscription.subscriptionStatus + + for status in statuses { + if case let .verified(subscription) = status.subscriptionRenewalInfo, + subscription.currentProductID == storeProduct.productIdentifier + { + if status.renewalState == .subscribed { + return true + } + } + } + + return false + } +} diff --git a/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/ISubscriptionStatusVerifierTypeResolver.swift b/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/ISubscriptionStatusVerifierTypeResolver.swift new file mode 100644 index 000000000..4060a9b64 --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/ISubscriptionStatusVerifierTypeResolver.swift @@ -0,0 +1,10 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +protocol ISubscriptionStatusVerifierTypeResolver { + func resolve(_ type: SubscriptionStatusVerifierType) -> ISubscriptionStatusVerifier? +} diff --git a/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/SubscriptionStatusVerifierTypeResolver.swift b/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/SubscriptionStatusVerifierTypeResolver.swift new file mode 100644 index 000000000..184b0952a --- /dev/null +++ b/Sources/FlareUI/Classes/Core/Resolvers/SubscriptionStatusVerifierTypeResolver/SubscriptionStatusVerifierTypeResolver.swift @@ -0,0 +1,22 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +final class SubscriptionStatusVerifierTypeResolver: ISubscriptionStatusVerifierTypeResolver { + // MARK: ISubscriptionStatusVerifierTypeResolver + + func resolve(_ type: SubscriptionStatusVerifierType) -> (any ISubscriptionStatusVerifier)? { + switch type { + case .automatic: + if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { + return SubscriptionStatusVerifier() + } + return nil + case let .custom(subscriptionVerifier): + return subscriptionVerifier + } + } +} diff --git a/Sources/FlareUI/Classes/DI/Dependencies/FlareDependencies.swift b/Sources/FlareUI/Classes/DI/Dependencies/FlareDependencies.swift new file mode 100644 index 000000000..bab6bcfd9 --- /dev/null +++ b/Sources/FlareUI/Classes/DI/Dependencies/FlareDependencies.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +final class FlareDependencies: IFlareDependencies { + // MARK: IFlareDependencies + + lazy var configurationProvider: IConfigurationProvider = ConfigurationProvider() + + var iap: IFlare { + Flare.shared + } + + var subscriptionStatusVerifierProvider: ISubscriptionStatusVerifierProvider { + SubscriptionStatusVerifierProvider( + configurationProvider: self.configurationProvider, + subscriptionStatusVerifierResolver: self.subscriptionStatusVerifierResolver + ) + } + + // MARK: Private + + private var subscriptionStatusVerifierResolver: ISubscriptionStatusVerifierTypeResolver { + SubscriptionStatusVerifierTypeResolver() + } +} diff --git a/Sources/FlareUI/Classes/DI/Dependencies/IFlareDependencies.swift b/Sources/FlareUI/Classes/DI/Dependencies/IFlareDependencies.swift new file mode 100644 index 000000000..f8238e0c9 --- /dev/null +++ b/Sources/FlareUI/Classes/DI/Dependencies/IFlareDependencies.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +/// A type defines dependencies for the package. +protocol IFlareDependencies { + /// An IAP manager. + var iap: IFlare { get } + + var configurationProvider: IConfigurationProvider { get } + + var subscriptionStatusVerifierProvider: ISubscriptionStatusVerifierProvider { get } +} diff --git a/Sources/FlareUI/Classes/DI/PresentationAssembly/IPresentationAssembly.swift b/Sources/FlareUI/Classes/DI/PresentationAssembly/IPresentationAssembly.swift new file mode 100644 index 000000000..11b81c5ca --- /dev/null +++ b/Sources/FlareUI/Classes/DI/PresentationAssembly/IPresentationAssembly.swift @@ -0,0 +1,18 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +/// A type defines a presentation assembly. +protocol IPresentationAssembly { + /// A products view assembly. + var productsViewAssembly: IProductsViewAssembly { get } + + /// A subscriptions view assembly. + var subscritpionsViewAssembly: ISubscriptionsAssembly { get } + + /// A product view assembly. + var productViewAssembly: IProductViewAssembly { get } +} diff --git a/Sources/FlareUI/Classes/DI/PresentationAssembly/PresentationAssembly.swift b/Sources/FlareUI/Classes/DI/PresentationAssembly/PresentationAssembly.swift new file mode 100644 index 000000000..1f6df3549 --- /dev/null +++ b/Sources/FlareUI/Classes/DI/PresentationAssembly/PresentationAssembly.swift @@ -0,0 +1,58 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +@available(watchOS, unavailable) +final class PresentationAssembly: IPresentationAssembly { + // MARK: Properties + + private let dependencies: IFlareDependencies + + // MARK: Initialization + + init(dependencies: IFlareDependencies = FlareDependencies()) { + self.dependencies = dependencies + } + + // MARK: IPresentationAssembly + + var productsViewAssembly: IProductsViewAssembly { + ProductsViewAssembly( + productAssembly: productViewAssembly, + storeButtonsAssembly: storeButtonsAssembly, + iap: dependencies.iap + ) + } + + var productViewAssembly: IProductViewAssembly { + ProductViewAssembly(iap: dependencies.iap) + } + + var subscritpionsViewAssembly: ISubscriptionsAssembly { + SubscriptionsAssembly( + iap: dependencies.iap, + storeButtonsAssembly: storeButtonsAssembly, + subscriptionStatusVerifierProvider: dependencies.subscriptionStatusVerifierProvider + ) + } + + // MARK: Private + + private var storeButtonAssembly: IStoreButtonAssembly { + StoreButtonAssembly(iap: dependencies.iap) + } + + private var storeButtonsAssembly: IStoreButtonsAssembly { + StoreButtonsAssembly( + storeButtonAssembly: storeButtonAssembly, + policiesButtonAssembly: policiesButtonAssembly + ) + } + + private var policiesButtonAssembly: IPoliciesButtonAssembly { + PoliciesButtonAssembly() + } +} diff --git a/Sources/FlareUI/Classes/FlareUI.swift b/Sources/FlareUI/Classes/FlareUI.swift new file mode 100644 index 000000000..7931934ef --- /dev/null +++ b/Sources/FlareUI/Classes/FlareUI.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public final class FlareUI: IFlareUI { + // MARK: Properties + + private let dependencies: IFlareDependencies + + private let configurationProvider: IConfigurationProvider + + /// The singleton instance. + private static let flareUI: FlareUI = .init() + + /// Returns a shared `Flare` object. + public static var shared: IFlareUI { flareUI } + + // MARK: Initialization + + init(dependencies: IFlareDependencies = FlareDependencies()) { + self.dependencies = dependencies + self.configurationProvider = dependencies.configurationProvider + } + + // MARK: Public + + /// Configures the FlareUI package with the provided configuration. + /// + /// - Parameters: + /// - configuration: The configuration object containing settings for FlareUI. + public static func configure(with configuration: UIConfiguration) { + flareUI.configurationProvider.configure(with: configuration) + } +} diff --git a/Sources/FlareUI/Classes/Generated/Colors.swift b/Sources/FlareUI/Classes/Generated/Colors.swift new file mode 100644 index 000000000..f96d24c60 --- /dev/null +++ b/Sources/FlareUI/Classes/Generated/Colors.swift @@ -0,0 +1,109 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +#if os(macOS) + import AppKit +#elseif os(iOS) + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif +#if canImport(SwiftUI) + import SwiftUI +#endif + +// Deprecated typealiases +@available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0") +internal typealias AssetColorTypeAlias = ColorAsset.Color + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +internal enum Asset { + internal enum Colors { + internal static let dynamicBackground = ColorAsset(name: "Colors/dynamic_background") + internal static let gray = ColorAsset(name: "Colors/gray") + internal static let systemBackground = ColorAsset(name: "Colors/system_background") + } +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details + +internal final class ColorAsset { + internal fileprivate(set) var name: String + + #if os(macOS) + internal typealias Color = NSColor + #elseif os(iOS) || os(tvOS) || os(watchOS) + internal typealias Color = UIColor + #endif + + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + internal private(set) lazy var color: Color = { + guard let color = Color(asset: self) else { + fatalError("Unable to load color asset named \(name).") + } + return color + }() + + #if os(iOS) || os(tvOS) + @available(iOS 11.0, tvOS 11.0, *) + internal func color(compatibleWith traitCollection: UITraitCollection) -> Color { + let bundle = BundleToken.bundle + guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load color asset named \(name).") + } + return color + } + #endif + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + internal private(set) lazy var swiftUIColor: SwiftUI.Color = { + SwiftUI.Color(asset: self) + }() + #endif + + fileprivate init(name: String) { + self.name = name + } +} + +internal extension ColorAsset.Color { + @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *) + convenience init?(asset: ColorAsset) { + let bundle = BundleToken.bundle + #if os(iOS) || os(tvOS) + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSColor.Name(asset.name), bundle: bundle) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +internal extension SwiftUI.Color { + init(asset: ColorAsset) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle) + } +} +#endif + +// swiftlint:disable convenience_type +private final class BundleToken { + static let bundle: Bundle = { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: BundleToken.self) + #endif + }() +} +// swiftlint:enable convenience_type diff --git a/Sources/FlareUI/Classes/Generated/Media.swift b/Sources/FlareUI/Classes/Generated/Media.swift new file mode 100644 index 000000000..846403da6 --- /dev/null +++ b/Sources/FlareUI/Classes/Generated/Media.swift @@ -0,0 +1,126 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +#if os(macOS) + import AppKit +#elseif os(iOS) + import UIKit +#elseif os(tvOS) || os(watchOS) + import UIKit +#endif +#if canImport(SwiftUI) + import SwiftUI +#endif + +// Deprecated typealiases +@available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0") +internal typealias AssetImageTypeAlias = ImageAsset.Image + +// swiftlint:disable superfluous_disable_command file_length implicit_return + +// MARK: - Asset Catalogs + +// swiftlint:disable identifier_name line_length nesting type_body_length type_name +internal enum Media { + internal enum Media { + internal static let checkmark = ImageAsset(name: "Media/checkmark") + internal static let circle = ImageAsset(name: "Media/circle") + internal static let star = ImageAsset(name: "Media/star") + } +} +// swiftlint:enable identifier_name line_length nesting type_body_length type_name + +// MARK: - Implementation Details + +internal struct ImageAsset { + internal fileprivate(set) var name: String + + #if os(macOS) + internal typealias Image = NSImage + #elseif os(iOS) || os(tvOS) || os(watchOS) + internal typealias Image = UIImage + #endif + + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *) + internal var image: Image { + let bundle = BundleToken.bundle + #if os(iOS) || os(tvOS) + let image = Image(named: name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + let name = NSImage.Name(self.name) + let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name) + #elseif os(watchOS) + let image = Image(named: name) + #endif + guard let result = image else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + + #if os(iOS) || os(tvOS) + @available(iOS 8.0, tvOS 9.0, *) + internal func image(compatibleWith traitCollection: UITraitCollection) -> Image { + let bundle = BundleToken.bundle + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif + + #if canImport(SwiftUI) + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) + internal var swiftUIImage: SwiftUI.Image { + SwiftUI.Image(asset: self) + } + #endif +} + +internal extension ImageAsset.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) + @available(macOS, deprecated, + message: "This initializer is unsafe on macOS, please use the ImageAsset.image property") + convenience init?(asset: ImageAsset) { + #if os(iOS) || os(tvOS) + let bundle = BundleToken.bundle + self.init(named: asset.name, in: bundle, compatibleWith: nil) + #elseif os(macOS) + self.init(named: NSImage.Name(asset.name)) + #elseif os(watchOS) + self.init(named: asset.name) + #endif + } +} + +#if canImport(SwiftUI) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +internal extension SwiftUI.Image { + init(asset: ImageAsset) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle) + } + + init(asset: ImageAsset, label: Text) { + let bundle = BundleToken.bundle + self.init(asset.name, bundle: bundle, label: label) + } + + init(decorative asset: ImageAsset) { + let bundle = BundleToken.bundle + self.init(decorative: asset.name, bundle: bundle) + } +} +#endif + +// swiftlint:disable convenience_type +private final class BundleToken { + static let bundle: Bundle = { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: BundleToken.self) + #endif + }() +} +// swiftlint:enable convenience_type diff --git a/Sources/FlareUI/Classes/Generated/Strings.swift b/Sources/FlareUI/Classes/Generated/Strings.swift new file mode 100644 index 000000000..b1cf247b1 --- /dev/null +++ b/Sources/FlareUI/Classes/Generated/Strings.swift @@ -0,0 +1,127 @@ +// swiftlint:disable all +// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen + +import Foundation + +// swiftlint:disable superfluous_disable_command file_length implicit_return prefer_self_in_static_references + +// MARK: - Strings + +// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces +internal enum L10n { + internal enum Common { + /// Privacy Policy + internal static let privacyPolicy = L10n.tr("Localizable", "common.privacy_policy", fallback: "Privacy Policy") + /// Terms of Service + internal static let termsOfService = L10n.tr("Localizable", "common.terms_of_service", fallback: "Terms of Service") + internal enum Subscription { + internal enum Action { + /// Subscribe + internal static let subscribe = L10n.tr("Localizable", "common.subscription.action.subscribe", fallback: "Subscribe") + } + internal enum Status { + /// Your current plan + internal static let yourCurrentPlan = L10n.tr("Localizable", "common.subscription.status.your_current_plan", fallback: "Your current plan") + /// Your plan + internal static let yourPlan = L10n.tr("Localizable", "common.subscription.status.your_plan", fallback: "Your plan") + } + } + internal enum Words { + /// and + internal static let and = L10n.tr("Localizable", "common.words.and", fallback: "and") + } + } + internal enum Error { + internal enum Default { + /// Error Occurred + internal static let title = L10n.tr("Localizable", "error.default.title", fallback: "Error Occurred") + } + } + internal enum Policies { + internal enum Unavailable { + internal enum PrivacyPolicy { + /// Something went wrong. Try again. + internal static let message = L10n.tr("Localizable", "policies.unavailable.privacy_policy.message", fallback: "Something went wrong. Try again.") + /// Privacy Policy Unavailable + internal static let title = L10n.tr("Localizable", "policies.unavailable.privacy_policy.title", fallback: "Privacy Policy Unavailable") + } + internal enum TermsOfService { + /// Something went wrong. Try again. + internal static let message = L10n.tr("Localizable", "policies.unavailable.terms_of_service.message", fallback: "Something went wrong. Try again.") + /// Terms of Service Unavailable + internal static let title = L10n.tr("Localizable", "policies.unavailable.terms_of_service.title", fallback: "Terms of Service Unavailable") + } + } + } + internal enum Product { + /// Every %@ + internal static func priceDescription(_ p1: Any) -> String { + return L10n.tr("Localizable", "product.price_description", String(describing: p1), fallback: "Every %@") + } + internal enum Subscription { + /// %@/%@ + internal static func price(_ p1: Any, _ p2: Any) -> String { + return L10n.tr("Localizable", "product.subscription.price", String(describing: p1), String(describing: p2), fallback: "%@/%@") + } + } + } + internal enum StoreButton { + /// Restore Missing Purchases + internal static let restorePurchases = L10n.tr("Localizable", "store_button.restore_purchases", fallback: "Restore Missing Purchases") + } + internal enum StoreUnavailable { + /// Store Unavailable + internal static let title = L10n.tr("Localizable", "store_unavailable.title", fallback: "Store Unavailable") + internal enum Product { + /// No in-app purchases are available in the current storefront. + internal static let message = L10n.tr("Localizable", "store_unavailable.product.message", fallback: "No in-app purchases are available in the current storefront.") + } + internal enum Subscription { + /// The subscription is unavailable in the current storefront. + internal static let message = L10n.tr("Localizable", "store_unavailable.subscription.message", fallback: "The subscription is unavailable in the current storefront.") + } + } + internal enum Subscription { + internal enum Loading { + /// Loading Subscriptions... + internal static let message = L10n.tr("Localizable", "subscription.loading.message", fallback: "Loading Subscriptions...") + } + } + internal enum Subscriptions { + internal enum Renewable { + /// Plan auto-renews for %@ until cancelled. + internal static func subscriptionDescription(_ p1: Any) -> String { + return L10n.tr("Localizable", "subscriptions.renewable.subscription_description", String(describing: p1), fallback: "Plan auto-renews for %@ until cancelled.") + } + /// Plan auto-renews for %@ + /// until cancelled. + internal static func subscriptionDescriptionSeparated(_ p1: Any) -> String { + return L10n.tr("Localizable", "subscriptions.renewable.subscription_description_separated", String(describing: p1), fallback: "Plan auto-renews for %@\nuntil cancelled.") + } + } + } +} +// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length +// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces + +// MARK: - Implementation Details + +extension L10n { + private static func tr(_ table: String, _ key: String, _ args: CVarArg..., fallback value: String) -> String { + let format = BundleToken.bundle.localizedString(forKey: key, value: value, table: table) + return String(format: format, locale: Locale.current, arguments: args) + } +} + +// swiftlint:disable convenience_type +private final class BundleToken { + static let bundle: Bundle = { + #if SWIFT_PACKAGE + return Bundle.module + #else + return Bundle(for: BundleToken.self) + #endif + }() +} +// swiftlint:enable convenience_type diff --git a/Sources/FlareUI/Classes/IFlareUI.swift b/Sources/FlareUI/Classes/IFlareUI.swift new file mode 100644 index 000000000..4bc6240f0 --- /dev/null +++ b/Sources/FlareUI/Classes/IFlareUI.swift @@ -0,0 +1,8 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public protocol IFlareUI {} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/BaseHostingController/BaseHostingController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/BaseHostingController/BaseHostingController.swift new file mode 100644 index 000000000..e2e12e3a5 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/BaseHostingController/BaseHostingController.swift @@ -0,0 +1,39 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if os(iOS) || os(macOS) + import SwiftUI + + @available(watchOS, unavailable) + class BaseHostingController: HostingController { + // MARK: Initialization + + override init?(coder aDecoder: NSCoder, rootView: View) { + super.init(coder: aDecoder, rootView: rootView) + setupUI() + } + + override init(rootView: View) { + super.init(rootView: rootView) + setupUI() + } + + @MainActor dynamic required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupUI() + } + + // MARK: Private + + private func setupUI() { + #if os(iOS) || os(tvOS) + self.view.backgroundColor = .clear + #elseif os(macOS) + self.view.wantsLayer = true + self.view.layer?.backgroundColor = .clear + #endif + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/ColorRepresentation.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/ColorRepresentation.swift new file mode 100644 index 000000000..3c272328c --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/ColorRepresentation.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +// swiftlint:disable file_types_order + +#if canImport(UIKit) + import UIKit +#elseif canImport(AppKit) + import AppKit +#endif + +#if os(iOS) || os(tvOS) + public typealias ColorRepresentation = UIKit.UIColor +#elseif os(macOS) + public typealias ColorRepresentation = NSColor +#endif +// swiftlint:enable file_types_order diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/SUIViewWrapper.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/SUIViewWrapper.swift new file mode 100644 index 000000000..47429cd37 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/Helpers/SUIViewWrapper.swift @@ -0,0 +1,66 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +#if os(iOS) || os(tvOS) + public typealias ViewRepresentation = UIKit.UIView +#elseif os(macOS) + public typealias ViewRepresentation = NSView +#elseif os(watchOS) + public typealias ViewRepresentation = WKInterfaceObject +#endif + +// MARK: - SUIViewWrapper + +struct SUIViewWrapper: ViewRepresentable { + // MARK: Types + + #if os(iOS) || os(tvOS) + typealias UIViewType = CustomView + #elseif os(macOS) + typealias NSViewType = CustomView + #elseif os(watchOS) + typealias WKInterfaceObjectType = CustomView + #endif + + // MARK: Properties + + private let view: CustomView + + // MARK: Initialization + + init(view: CustomView) { + self.view = view + } + + // MARK: ViewRepresentable + + #if os(macOS) + func makeNSView(context _: Context) -> CustomView { + view + } + + func updateNSView(_: NSViewType, context _: Context) {} + #endif + + #if os(iOS) || os(tvOS) + func makeUIView(context _: Context) -> CustomView { + view + } + + func updateUIView(_: UIViewType, context _: Context) {} + #endif + + #if os(watchOS) + func makeWKInterfaceObject(context _: Context) -> CustomView { + view + } + + func makeCoordinator() {} + + func updateWKInterfaceObject(_: CustomView, context _: Context) {} + #endif +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewController.swift new file mode 100644 index 000000000..adfbf4e70 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewController.swift @@ -0,0 +1,79 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import StoreKit +import SwiftUI + +// MARK: - ProductViewController + +#if os(iOS) + @available(watchOS, unavailable) + public final class ProductViewController: ViewController { + // MARK: - Properties + + private lazy var viewModel = ProductViewControllerViewModel() + + private lazy var productView: HostingController = { + let view = ProductView(id: self.id) + .onInAppPurchaseCompletion(completion: viewModel.onInAppPurchaseCompletion) + .inAppPurchaseOptions(viewModel.inAppPurchaseOptions) + .productViewStyle(viewModel.productStyle) + + return BaseHostingController(rootView: view) + }() + + private let id: String + + public var onInAppPurchaseCompletion: PurchaseCompletionHandler? { + didSet { + viewModel.onInAppPurchaseCompletion = onInAppPurchaseCompletion + } + } + + public var productStyle: any IProductStyle = CompactProductStyle() { + didSet { + viewModel.productStyle = AnyProductStyle(style: productStyle) + } + } + + // MARK: Initialization + + public init(id: String) { + self.id = id + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + public required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Life Cycle + + override public func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: Private + + private func setupUI() { + #if os(iOS) || os(tvOS) + self.view.backgroundColor = Asset.Colors.systemBackground.color + #endif + self.add(productView) + } + } + + // MARK: - Environments + + public extension ProductViewController { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func inAppPurchaseOptions(_ options: ((StoreProduct) -> Set?)?) { + viewModel.inAppPurchaseOptions = { PurchaseOptions(options: options?($0) ?? []) } + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewControllerViewModel.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewControllerViewModel.swift new file mode 100644 index 000000000..d6220ae17 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductViewController/ProductViewControllerViewModel.swift @@ -0,0 +1,14 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +@available(watchOS, unavailable) +final class ProductViewControllerViewModel: ObservableObject { + @Published var onInAppPurchaseCompletion: PurchaseCompletionHandler? + @Published var inAppPurchaseOptions: PurchaseOptionHandler? + @Published var productStyle = AnyProductStyle(style: CompactProductStyle()) +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewController.swift new file mode 100644 index 000000000..673e2d3c0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewController.swift @@ -0,0 +1,105 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import StoreKit +import SwiftUI +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +// MARK: - ProductsViewController + +#if os(iOS) + @available(watchOS, unavailable) + public final class ProductsViewController: ViewController { + // MARK: - Properties + + private lazy var viewModel = ProductsViewControllerViewModel() + + private lazy var productsView: HostingController = { + let view = ProductsView(ids: self.ids) + .onInAppPurchaseCompletion(completion: viewModel.onInAppPurchaseCompletion) + .storeButton(.visible, types: viewModel.visibleStoreButtons) + .storeButton(.hidden, types: viewModel.hiddenStoreButtons) + .inAppPurchaseOptions(viewModel.inAppPurchaseOptions) + .productViewStyle(viewModel.productStyle) + + return BaseHostingController(rootView: view) + }() + + private let ids: any Collection + + public var onInAppPurchaseCompletion: PurchaseCompletionHandler? { + didSet { + viewModel.onInAppPurchaseCompletion = onInAppPurchaseCompletion + } + } + + public var productStyle: any IProductStyle = CompactProductStyle() { + didSet { + viewModel.productStyle = AnyProductStyle(style: productStyle) + } + } + + // MARK: Initialization + + public init(ids: some Collection) { + self.ids = ids + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + public required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Life Cycle + + override public func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: Private + + private func setupUI() { + #if os(iOS) || os(tvOS) + self.view.backgroundColor = Asset.Colors.systemBackground.color + #endif + self.add(productsView) + } + + private func updateStoreButtons( + _ buttons: inout [StoreButtonType], + add newButtons: [StoreButtonType] + ) { + buttons += newButtons + buttons = buttons.removingDuplicates() + } + } + + // MARK: - Environments + + public extension ProductsViewController { + func storeButton(_ visibility: StoreButtonVisibility, types: [StoreButtonType]) { + switch visibility { + case .visible: + updateStoreButtons(&viewModel.visibleStoreButtons, add: types) + viewModel.hiddenStoreButtons.removeAll { types.contains($0) } + case .hidden: + updateStoreButtons(&viewModel.hiddenStoreButtons, add: types) + viewModel.visibleStoreButtons.removeAll { types.contains($0) } + } + } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func inAppPurchaseOptions(_ options: ((StoreProduct) -> Set?)?) { + viewModel.inAppPurchaseOptions = { PurchaseOptions(options: options?($0) ?? []) } + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewControllerViewModel.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewControllerViewModel.swift new file mode 100644 index 000000000..7e028bbf0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ProductsViewController/ProductsViewControllerViewModel.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +@available(watchOS, unavailable) +final class ProductsViewControllerViewModel: ObservableObject { + @Published var onInAppPurchaseCompletion: PurchaseCompletionHandler? + @Published var visibleStoreButtons: [StoreButtonType] = [] + @Published var hiddenStoreButtons: [StoreButtonType] = [] + @Published var inAppPurchaseOptions: PurchaseOptionHandler? + @Published var productStyle = AnyProductStyle(style: CompactProductStyle()) +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewController.swift new file mode 100644 index 000000000..0a83af672 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewController.swift @@ -0,0 +1,193 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import StoreKit +import SwiftUI + +// MARK: - SubscriptionsViewController + +#if os(iOS) + @available(iOS 13.0, macOS 11.0, *) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public final class SubscriptionsViewController: ViewController { + // MARK: Properties + + private lazy var viewModel = SubscriptionsViewControllerViewModel() + + private lazy var subscriptionsView: HostingController = { + let view = SubscriptionsView(ids: self.ids) + .onInAppPurchaseCompletion(completion: viewModel.onInAppPurchaseCompletion) + .inAppPurchaseOptions(viewModel.inAppPurchaseOptions) + .subscriptionControlStyle(viewModel.subscriptionControlStyle) + .subscriptionBackground(viewModel.subscriptionBackgroundColor) + .subscriptionViewTint(viewModel.subscriptionViewTintColor) + .subscriptionButtonLabel(viewModel.subscriptionButtonLabelStyle) + .storeButton(.visible, types: viewModel.visibleStoreButtons) + .storeButton(.hidden, types: viewModel.hiddenStoreButtons) + .subscriptionMarketingContent { viewModel.marketingContent } + #if os(iOS) || os(tvOS) + .subscriptionHeaderContentBackground(viewModel.subscriptionHeaderContentBackground) + #endif + #if os(iOS) + .subscriptionPrivacyPolicyURL(viewModel.subscriptionPrivacyPolicyURL) + .subscriptionTermsOfServiceURL(viewModel.subscriptionTermsOfServiceURL) + #endif + + return BaseHostingController(rootView: view) + }() + + private let ids: any Collection + + public var onInAppPurchaseCompletion: PurchaseCompletionHandler? { + didSet { + viewModel.onInAppPurchaseCompletion = onInAppPurchaseCompletion + } + } + + public var subscriptionControlStyle: any ISubscriptionControlStyle = AutomaticSubscriptionControlStyle() { + didSet { + viewModel.subscriptionControlStyle = AnySubscriptionControlStyle(style: subscriptionControlStyle) + } + } + + public var subscriptionBackgroundColor: ColorRepresentation = .clear { + didSet { + viewModel.subscriptionBackgroundColor = Color(subscriptionBackgroundColor) + } + } + + public var subscriptionViewTintColor: ColorRepresentation = .blue { + didSet { + viewModel.subscriptionViewTintColor = Color(subscriptionViewTintColor) + } + } + + public var subscriptionButtonLabelStyle: SubscriptionStoreButtonLabel = .action { + didSet { + viewModel.subscriptionButtonLabelStyle = subscriptionButtonLabelStyle + } + } + + public var subscriptionMarketingContnentView: ViewRepresentation? { + didSet { + guard let subscriptionMarketingContnentView else { + viewModel.marketingContent = nil + return + } + viewModel.marketingContent = SUIViewWrapper( + view: subscriptionMarketingContnentView + ) + .eraseToAnyView() + } + } + + #if os(iOS) || os(tvOS) + public var subscriptionHeaderContentBackground: ColorRepresentation = .clear { + didSet { + viewModel.subscriptionHeaderContentBackground = Color(subscriptionHeaderContentBackground) + } + } + #endif + + #if os(iOS) + public var subscriptionPrivacyPolicyURL: URL? { + didSet { + viewModel.subscriptionPrivacyPolicyURL = subscriptionPrivacyPolicyURL + } + } + + public var subscriptionTermsOfServiceURL: URL? { + didSet { + viewModel.subscriptionTermsOfServiceURL = subscriptionTermsOfServiceURL + } + } + #endif + + public var subscriptionPrivacyPolicyView: ViewRepresentation? { + didSet { + guard let subscriptionPrivacyPolicyView else { + self.viewModel.subscriptionPrivacyPolicyView = nil + return + } + viewModel.subscriptionPrivacyPolicyView = SUIViewWrapper( + view: subscriptionPrivacyPolicyView + ).eraseToAnyView() + } + } + + public var subscriptionTermsOfServiceView: ViewRepresentation? { + didSet { + guard let subscriptionTermsOfServiceView else { + self.viewModel.subscriptionTermsOfServiceView = nil + return + } + viewModel.subscriptionTermsOfServiceView = SUIViewWrapper( + view: subscriptionTermsOfServiceView + ).eraseToAnyView() + } + } + + // MARK: Initialization + + public init(ids: any Collection) { + self.ids = ids + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + public required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Life Cycle + + override public func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: Private + + private func setupUI() { + #if os(iOS) || os(tvOS) + self.view.backgroundColor = Asset.Colors.systemBackground.color + #endif + self.add(subscriptionsView) + } + + private func updateStoreButtons( + _ buttons: inout [StoreButtonType], + add newButtons: [StoreButtonType] + ) { + buttons += newButtons + buttons = buttons.removingDuplicates() + } + } + + // MARK: - Environments + + @available(iOS 13.0, macOS 11.0, tvOS 13.0, *) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public extension SubscriptionsViewController { + func storeButton(_ visibility: StoreButtonVisibility, types: [StoreButtonType]) { + switch visibility { + case .visible: + updateStoreButtons(&viewModel.visibleStoreButtons, add: types) + viewModel.hiddenStoreButtons.removeAll { types.contains($0) } + case .hidden: + updateStoreButtons(&viewModel.hiddenStoreButtons, add: types) + viewModel.visibleStoreButtons.removeAll { types.contains($0) } + } + } + + @available(iOS 15.0, tvOS 15.0, macOS 12.0, *) + func inAppPurchaseOptions(_ options: ((StoreProduct) -> Set?)?) { + viewModel.inAppPurchaseOptions = { PurchaseOptions(options: options?($0) ?? []) } + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewControllerViewModel.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewControllerViewModel.swift new file mode 100644 index 000000000..bad85892b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/SubscriptionsViewController/SubscriptionsViewControllerViewModel.swift @@ -0,0 +1,31 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation +import SwiftUI + +@available(iOS 13.0, macOS 11.0, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +final class SubscriptionsViewControllerViewModel: ObservableObject { + @Published var onInAppPurchaseCompletion: PurchaseCompletionHandler? + @Published var inAppPurchaseOptions: PurchaseOptionHandler? + @Published var marketingContent: AnyView? + @Published var subscriptionButtonLabelStyle: SubscriptionStoreButtonLabel = .action + @Published var subscriptionBackgroundColor: Color = .clear + @Published var subscriptionViewTintColor: Color = .blue + @Published var subscriptionControlStyle: AnySubscriptionControlStyle = .init(style: AutomaticSubscriptionControlStyle()) + @Published var visibleStoreButtons: [StoreButtonType] = [] + @Published var hiddenStoreButtons: [StoreButtonType] = [] + #if os(iOS) || os(tvOS) + @Published var subscriptionHeaderContentBackground: Color = .clear + #endif + #if os(iOS) + @Published var subscriptionPrivacyPolicyURL: URL? + @Published var subscriptionTermsOfServiceURL: URL? + #endif + @Published var subscriptionPrivacyPolicyView: AnyView? + @Published var subscriptionTermsOfServiceView: AnyView? +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/HostingController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/HostingController.swift new file mode 100644 index 000000000..d49194d5e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/HostingController.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +import SwiftUI + +#if os(iOS) || os(tvOS) + typealias HostingController = UIHostingController +#elseif os(macOS) + typealias HostingController = NSHostingController +#elseif os(watchOS) + typealias HostingController = WKHostingController +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/ViewController.swift b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/ViewController.swift new file mode 100644 index 000000000..25b583ed0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Controllers/ViewController/ViewController.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +import SwiftUI + +#if os(iOS) || os(tvOS) + public typealias ViewController = UIViewController +#elseif os(macOS) + public typealias ViewController = NSViewController +#elseif os(watchOS) + public typealias ViewController = WKInterfaceController +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/Palette.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/Palette.swift new file mode 100644 index 000000000..9766e7c05 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/Palette.swift @@ -0,0 +1,62 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if os(iOS) + import UIKit +#elseif os(macOS) + import Cocoa +#endif + +import SwiftUI + +// MARK: - Palette + +enum Palette { + static var gray: Color { + Asset.Colors.gray.swiftUIColor + } + + static var dynamicBackground: Color { + Asset.Colors.dynamicBackground.swiftUIColor + } + + static var systemBackground: Color { + Asset.Colors.systemBackground.swiftUIColor + } + + static var systemGray5: Color { + #if os(iOS) + Color(UIColor.systemGray5) + #else + systemGray + #endif + } + + static var systemGray2: Color { + #if os(iOS) + Color(UIColor.systemGray2) + #else + systemGray + #endif + } + + static var systemGray4: Color { + #if os(iOS) + Color(UIColor.systemGray4) + #else + systemGray + #endif + } + + static var systemGray: Color { + #if os(macOS) + Color(NSColor.systemGray) + #elseif os(watchOS) + Color(UIColor.gray) + #else + Color(UIColor.systemGray) + #endif + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/UIConstants.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/UIConstants.swift new file mode 100644 index 000000000..4795a5513 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Constants/UIConstants.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +extension CGFloat { + /// 4px + static let cornerRadius4px = 4.0 +} + +extension CGFloat { + /// 10px + static let spacing10px = 10.0 +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonType.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonType.swift new file mode 100644 index 000000000..cf6e26f95 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonType.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - StoreButtonType + +public enum StoreButtonType { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + case restore + + case policies +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonVisibility.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonVisibility.swift new file mode 100644 index 000000000..dea49919e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Models/StoreButtonVisibility.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - StoreButtonVisibility + +public enum StoreButtonVisibility { + case visible, hidden +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IModel.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IModel.swift new file mode 100644 index 000000000..800d8b78c --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IModel.swift @@ -0,0 +1,22 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +/// A type that represents a default model object. +protocol IModel { + /// The associated type representing the state of the model. + associatedtype State + + /// The current state of the model. + var state: State { get } + + /// Function to set the state of the model and return a new instance with the updated state. + /// + /// - Parameter state: The new state to set. + /// + /// - Returns: A new instance of the model with the updated state. + func setState(_ state: State) -> Self +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IPresenter.swift b/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IPresenter.swift new file mode 100644 index 000000000..5eb44684a --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Core/Protocols/IPresenter.swift @@ -0,0 +1,39 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - IPresenter + +/// A protocol that defines the basic functionality of a presenter. +protocol IPresenter { + /// The associated type representing the model associated with the presenter. + associatedtype Model: IModel + + /// The view model associated with the presenter. + var viewModel: WrapperViewModel? { get } + + /// Updates the state of the presenter's model. + /// + /// - Parameters: + /// - state: The new state to update to. + /// - animation: The animation to use for the state update. + func update(state: Model.State, animation: Animation?) +} + +extension IPresenter { + /// Default implementation for updating the presenter's model state. + /// + /// - Parameters: + /// - state: The new state to update to. + /// - animation: The animation to use for the state update. + func update(state: Model.State, animation: Animation? = .default) { + guard let viewModel = viewModel else { return } + + withAnimation(animation) { + viewModel.model = viewModel.model.setState(state) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Factories/ISubscriptionPriceViewModelFactory.swift b/Sources/FlareUI/Classes/Presentation/Components/Factories/ISubscriptionPriceViewModelFactory.swift new file mode 100644 index 000000000..79d456161 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Factories/ISubscriptionPriceViewModelFactory.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +// MARK: - ISubscriptionPriceViewModelFactory + +protocol ISubscriptionPriceViewModelFactory { + func make(_ product: StoreProduct, format: PriceDisplayFormat) -> String + func period(from product: StoreProduct) -> String? +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Factories/SubscriptionPriceViewModelFactory.swift b/Sources/FlareUI/Classes/Presentation/Components/Factories/SubscriptionPriceViewModelFactory.swift new file mode 100644 index 000000000..dd3f50b89 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Factories/SubscriptionPriceViewModelFactory.swift @@ -0,0 +1,95 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +final class SubscriptionPriceViewModelFactory: ISubscriptionPriceViewModelFactory { + // MARK: Properties + + private var dateFormatter: IDateComponentsFormatter + private let subscriptionDateComponentsFactory: ISubscriptionDateComponentsFactory + + // MARK: Initialization + + init( + dateFormatter: IDateComponentsFormatter = DateComponentsFormatter.full, + subscriptionDateComponentsFactory: ISubscriptionDateComponentsFactory = SubscriptionDateComponentsFactory() + ) { + self.dateFormatter = dateFormatter + self.subscriptionDateComponentsFactory = subscriptionDateComponentsFactory + } + + // MARK: ISubscriptionPriceViewModelFactory + + func make(_ product: StoreProduct, format: PriceDisplayFormat) -> String { + makePrice(from: product, format: format) + } + + func period(from product: StoreProduct) -> String? { + guard let period = product.subscriptionPeriod else { return nil } + + let unit = makeUnit(from: period.unit) + dateFormatter.allowedUnits = [unit] + + let dateComponents = subscriptionDateComponentsFactory.dateComponents(for: period) + let localizedPeriod = dateFormatter.string(from: dateComponents) + + return localizedPeriod + } + + // MARK: Private + + private func makePrice(from product: StoreProduct, format: PriceDisplayFormat) -> String { + switch product.productType { + case .consumable, .nonConsumable, .nonRenewableSubscription: + return product.localizedPriceString ?? "" + case .autoRenewableSubscription: + guard let period = product.subscriptionPeriod else { return "" } + + switch format { + case .short: + return product.localizedPriceString ?? "" + case .full: + let unit = makeUnit(from: period.unit) + if unit == .day, period.value == 7 { + dateFormatter.allowedUnits = [.weekOfMonth] + } else { + dateFormatter.allowedUnits = [unit] + } + + let dateComponents = subscriptionDateComponentsFactory.dateComponents(for: period) + let localizedPeriod = dateFormatter.string(from: dateComponents) + + return [product.localizedPriceString, String(localizedPeriod?.words.last)] + .compactMap { $0 } + .joined(separator: "/") + } + case .none: + return "" + } + } + + private func makeUnit(from unit: SubscriptionPeriod.Unit) -> NSCalendar.Unit { + switch unit { + case .day: + return .day + case .week: + return .weekOfMonth + case .month: + return .month + case .year: + return .year + } + } + + private func makePriceDescription(from product: StoreProduct) -> String? { + let localizedPeriod = period(from: product) + + guard let string = localizedPeriod?.words.last else { return nil } + + return L10n.Product.priceDescription(string).capitalized + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Contrast.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Contrast.swift new file mode 100644 index 000000000..eea94fb18 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Contrast.swift @@ -0,0 +1,23 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// swiftlint:disable identifier_name +extension View { + func contrast( + _ backgroundColor: Color, + lightColor: Color = .white, + darkColor: Color = .black + ) -> some View { + var r, g, b, a: CGFloat + (r, g, b, a) = (0, 0, 0, 0) + backgroundColor.uiColor().getRed(&r, green: &g, blue: &b, alpha: &a) + let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b + return luminance < 0.6 ? foregroundColor(lightColor) : foregroundColor(darkColor) + } +} + +// swiftlint:enable identifier_name diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Paywall.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Paywall.swift new file mode 100644 index 000000000..9160f8ad0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+Paywall.swift @@ -0,0 +1,26 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +/// A SwiftUI extension to add a paywall to a view. +public extension View { + /// Adds a paywall to the view. + /// + /// - Parameters: + /// - presented: A binding to control the presentation state of the paywall. + /// - paywallType: The type of paywall to display. + /// + /// - Returns: A modified view with the paywall added. + @available(watchOS, unavailable) + func paywall(presented: Binding, paywallType: PaywallType) -> some View { + modifier( + PaywallViewModifier( + paywallType: paywallType, + presented: presented + ) + ) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+ProductViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+ProductViewStyle.swift new file mode 100644 index 000000000..76614c629 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+ProductViewStyle.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func productViewStyle(_ style: some IProductStyle) -> some View { + environment(\.productViewStyle, AnyProductStyle(style: style)) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseCompletion.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseCompletion.swift new file mode 100644 index 000000000..d1dff2b07 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseCompletion.swift @@ -0,0 +1,13 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +public extension View { + func onInAppPurchaseCompletion(completion: ((StoreProduct, Result) -> Void)?) -> some View { + environment(\.purchaseCompletion, completion) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseOption.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseOption.swift new file mode 100644 index 000000000..1a466c563 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+PurchaseOption.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import StoreKit +import SwiftUI + +extension View { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func inAppPurchaseOptions(_ options: ((StoreProduct) -> Set)?) -> some View { + environment(\.purchaseOptions) { PurchaseOptions(options: options?($0) ?? []) } + } + + func inAppPurchaseOptions(_ options: ((StoreProduct) -> PurchaseOptions)?) -> some View { + environment(\.purchaseOptions, options) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButton.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButton.swift new file mode 100644 index 000000000..2a655f07c --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButton.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func storeButton(_ visibility: StoreButtonVisibility, types: StoreButtonType...) -> some View { + transformEnvironment(\.storeButton) { values in + if visibility == .hidden { + values = values.filter { !types.contains($0) } + } else { + let types = types.removingDuplicates() + let diff = types.filter { !values.contains($0) } + values += diff + } + } + } + + func storeButton(_ visibility: StoreButtonVisibility, types: [StoreButtonType]) -> some View { + transformEnvironment(\.storeButton) { values in + if visibility == .hidden { + values = values.filter { !types.contains($0) } + } else { + let types = types.removingDuplicates() + let diff = types.filter { !values.contains($0) } + values += diff + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButtonViewFontWeight.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButtonViewFontWeight.swift new file mode 100644 index 000000000..f7209fc08 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+StoreButtonViewFontWeight.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +extension View { + func storeButtonViewFontWeight(_ weight: Font.Weight) -> some View { + environment(\.storeButtonViewFontWeight, weight) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionBackground.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionBackground.swift new file mode 100644 index 000000000..7829d7966 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionBackground.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, macOS 10.15, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionBackground(_ color: Color) -> some View { + environment(\.subscriptionBackground, color) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionControlStyle.swift new file mode 100644 index 000000000..a599affce --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionControlStyle.swift @@ -0,0 +1,23 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(watchOS, unavailable) + func subscriptionControlStyle(_ style: some ISubscriptionControlStyle) -> some View { + environment(\.subscriptionControlStyle, prepareStyle(style)) + } + + // MARK: Private + + private func prepareStyle(_ style: some ISubscriptionControlStyle) -> AnySubscriptionControlStyle { + if let style = style as? AnySubscriptionControlStyle { + return style + } else { + return AnySubscriptionControlStyle(style: style) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionHeaderContentBackground.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionHeaderContentBackground.swift new file mode 100644 index 000000000..4369535f0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionHeaderContentBackground.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionHeaderContentBackground(_ color: Color) -> some View { + environment(\.subscriptionHeaderContentBackground, color) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionMarketingContent.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionMarketingContent.swift new file mode 100644 index 000000000..faeee81d7 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionMarketingContent.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func subscriptionMarketingContent(@ViewBuilder view: () -> some View) -> some View { + environment(\.subscriptionMarketingContent, view().eraseToAnyView()) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPickerItemBackground.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPickerItemBackground.swift new file mode 100644 index 000000000..7559685d8 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPickerItemBackground.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, macOS 10.15, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionButtonLabel(_ style: SubscriptionStoreButtonLabel) -> some View { + environment(\.subscriptionStoreButtonLabel, style) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyDestination.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyDestination.swift new file mode 100644 index 000000000..d71921599 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyDestination.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func subscriptionPrivacyPolicyDestination(@ViewBuilder content: () -> some View) -> some View { + environment(\.subscriptionPrivacyPolicyDestination, content().eraseToAnyView()) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyURL.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyURL.swift new file mode 100644 index 000000000..bd6330abe --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionPrivacyPolicyURL.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionPrivacyPolicyURL(_ url: URL?) -> some View { + environment(\.subscriptionPrivacyPolicyURL, url) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionStoreButtonLabel.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionStoreButtonLabel.swift new file mode 100644 index 000000000..2a0335ae8 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionStoreButtonLabel.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, macOS 10.15, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionPickerItemBackground(_ backgroundStyle: Color) -> some View { + environment(\.subscriptionPickerItemBackground, backgroundStyle) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceDestination.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceDestination.swift new file mode 100644 index 000000000..69533b8e0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceDestination.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func subscriptionTermsOfServiceDestination(@ViewBuilder content: () -> some View) -> some View { + environment(\.subscriptionTermsOfServiceDestination, content().eraseToAnyView()) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceURL.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceURL.swift new file mode 100644 index 000000000..218bfb1dc --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionTermsOfServiceURL.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + func subscriptionTermsOfServiceURL(_ url: URL?) -> some View { + environment(\.subscriptionTermsOfServiceURL, url) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionViewTint.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionViewTint.swift new file mode 100644 index 000000000..af6011907 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+SubscriptionViewTint.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func subscriptionViewTint(_ color: Color) -> some View { + environment(\.subscriptionViewTint, color) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+TintColor.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+TintColor.swift new file mode 100644 index 000000000..3ae07d39f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/SUI/View+TintColor.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public extension View { + func tintColor(_ color: Color) -> some View { + environment(\.tintColor, color) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Helpers/UIKit/ViewController+Child.swift b/Sources/FlareUI/Classes/Presentation/Components/Helpers/UIKit/ViewController+Child.swift new file mode 100644 index 000000000..5d5d69d37 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Helpers/UIKit/ViewController+Child.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +#if os(iOS) || os(macOS) + extension ViewController { + func add(_ controller: ViewController) { + addChild(controller) + view.addSubview(controller.view) + + #if os(iOS) || os(tvOS) + controller.didMove(toParent: self) + #endif + + controller.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + controller.view.topAnchor.constraint(equalTo: self.view.topAnchor), + controller.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + controller.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + controller.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + ]) + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/BorderedButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/BorderedButtonStyle.swift new file mode 100644 index 000000000..785a02250 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/BorderedButtonStyle.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - BorderedButtonStyle + +struct BorderedButtonStyle: ButtonStyle { + // MARK: ButtonStyle + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .padding(.horizontal, .horizontalPadding) + .padding(.vertical, .verticalPadding) + .background(Palette.gray) + .foregroundColor(.blue) + .mask(Capsule()) + .opacity(configuration.isPressed ? 0.5 : 1.0) + } +} + +// MARK: Extensions + +extension ButtonStyle where Self == BorderedButtonStyle { + static var bordered: Self { + .init() + } +} + +// MARK: Constants + +private extension CGFloat { + static let horizontalPadding = 16.0 + static let verticalPadding = 8.0 +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/PrimaryButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/PrimaryButtonStyle.swift new file mode 100644 index 000000000..2001bc60e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/PrimaryButtonStyle.swift @@ -0,0 +1,43 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PrimaryButtonStyle + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct PrimaryButtonStyle: ButtonStyle { + // MARK: Properties + + @Environment(\.tintColor) private var tintColor + + // MARK: ButtonStyle + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .foregroundColor(.white) + .frame(height: 50.0) + .frame(maxWidth: .infinity) + .padding(.horizontal) + .background(tintColor) + .clipShape(RoundedRectangle(cornerSize: .init(width: 14, height: 14))) + .opacity(configuration.isPressed ? 0.5 : 1.0) + } +} + +// MARK: - Extensions + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +extension ButtonStyle where Self == PrimaryButtonStyle { + static var primary: PrimaryButtonStyle { + PrimaryButtonStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/CompactProductStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/CompactProductStyle.swift new file mode 100644 index 000000000..7f9e170c1 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/CompactProductStyle.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public struct CompactProductStyle: IProductStyle { + // MARK: Properties + + private var viewModelFactory: IProductViewModelFactory + + // MARK: Initialization + + public init() { + self.init(viewModelFactory: ProductViewModelFactory()) + } + + init(viewModelFactory: IProductViewModelFactory = ProductViewModelFactory()) { + self.viewModelFactory = viewModelFactory + } + + // MARK: IProductStyle + + @ViewBuilder + public func makeBody(configuration: ProductStyleConfiguration) -> some View { + switch configuration.state { + case .loading: + ProductPlaceholderView(isIconHidden: configuration.icon == nil, style: .compact) + case let .product(product): + let viewModel = viewModelFactory.make(product, style: .compact) + ProductInfoView(viewModel: viewModel, icon: configuration.icon, style: .compact) { configuration.purchase() } + case .error: + ProductPlaceholderView(isIconHidden: configuration.icon == nil, style: .compact) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Configuration/ProductStyleConfiguration.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Configuration/ProductStyleConfiguration.swift new file mode 100644 index 000000000..50664f44f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Configuration/ProductStyleConfiguration.swift @@ -0,0 +1,62 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +/// Configuration for the style of a product, including its state and purchase action. +public struct ProductStyleConfiguration { + // MARK: Types + + /// Represents the state of the product style. + public enum State { + /// The product is currently loading. + case loading + /// The product is available for purchase, with the specified store product item. + case product(item: StoreProduct) + /// An error occurred while loading the product. + case error(error: IAPError) + } + + /// Represents the icon view for the product. + public struct Icon: View { + // MARK: Properties + + /// The body of the icon view. + public var body: AnyView + + // MARK: Initialization + + /// Initializes the icon view with the specified content. + /// + /// - Parameter content: The content of the icon view. + public init(content: Content) { + body = AnyView(content) + } + } + + // MARK: Properties + + /// The icon view for the product. + public let icon: Icon? + /// The state of the product. + public let state: State + /// The purchase action. + public let purchase: () -> Void + + // MARK: Initialization + + /// Initializes the product style configuration with the specified parameters. + /// + /// - Parameters: + /// - icon: The icon view for the product. + /// - state: The state of the product. + /// - purchase: The purchase action. + public init(icon: Icon? = nil, state: State, purchase: @escaping () -> Void = {}) { + self.icon = icon + self.state = state + self.purchase = purchase + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/LargeProductStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/LargeProductStyle.swift new file mode 100644 index 000000000..5bffa4126 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/LargeProductStyle.swift @@ -0,0 +1,41 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13, *) +@available(macOS, unavailable) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +public struct LargeProductStyle: IProductStyle { + // MARK: Properties + + private var viewModelFactory: IProductViewModelFactory + + // MARK: Initialization + + public init() { + self.init(viewModelFactory: ProductViewModelFactory()) + } + + init(viewModelFactory: IProductViewModelFactory = ProductViewModelFactory()) { + self.viewModelFactory = viewModelFactory + } + + // MARK: IProductStyle + + @ViewBuilder + public func makeBody(configuration: ProductStyleConfiguration) -> some View { + switch configuration.state { + case .loading: + ProductPlaceholderView(isIconHidden: configuration.icon == nil, style: .large) + case let .product(product): + let viewModel = viewModelFactory.make(product, style: .large) + ProductInfoView(viewModel: viewModel, icon: configuration.icon, style: .large) { configuration.purchase() } + case .error: + ProductPlaceholderView(isIconHidden: configuration.icon == nil, style: .large) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Compact.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Compact.swift new file mode 100644 index 000000000..38963a578 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Compact.swift @@ -0,0 +1,12 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +public extension IProductStyle where Self == CompactProductStyle { + static var `default`: Self { + CompactProductStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Large.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Large.swift new file mode 100644 index 000000000..55755a75b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle+Large.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +@available(iOS 13.0, *) +@available(macOS, unavailable) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +public extension IProductStyle where Self == LargeProductStyle { + static var large: Self { + LargeProductStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle.swift new file mode 100644 index 000000000..2f1aeb613 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Product/Protocols/IProductStyle.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +/// A type that represents a product style. +public protocol IProductStyle { + /// The properties of an in-app store product. + typealias Configuration = ProductStyleConfiguration + + /// A view that represents the body of an in-app store product. + associatedtype Body: View + + /// Creates a view that represents the body of an in-app store product. + /// - Parameter configuration: The properties of an in-app store product. + @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Configuration/SubscriptionStoreControlStyleConfiguration.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Configuration/SubscriptionStoreControlStyleConfiguration.swift new file mode 100644 index 000000000..a0d506c25 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Configuration/SubscriptionStoreControlStyleConfiguration.swift @@ -0,0 +1,49 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// swiftlint:disable:next type_name +public struct SubscriptionStoreControlStyleConfiguration { + // MARK: Types + + struct Label: View { + var body: AnyView + + init(_ view: Content) { + body = view.eraseToAnyView() + } + } + + struct Description: View { + var body: AnyView + + init(_ view: Content) { + body = view.eraseToAnyView() + } + } + + struct Price: View { + var body: AnyView + + init(_ view: Content) { + body = view.eraseToAnyView() + } + } + + // MARK: Properties + + let label: Label + let description: Description + let price: Price + let isSelected: Bool + let isActive: Bool + + let action: () -> Void + + func trigger() { + action() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+Bordered.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+Bordered.swift new file mode 100644 index 000000000..3f8349e14 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+Bordered.swift @@ -0,0 +1,14 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +@available(watchOS, unavailable) +public extension ISubscriptionControlStyle where Self == ButtonSubscriptionStoreControlStyle { + static var button: ButtonSubscriptionStoreControlStyle { + ButtonSubscriptionStoreControlStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+PickerSubscriptionStoreControlStyle+PickerSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+PickerSubscriptionStoreControlStyle+PickerSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..0bdc2c863 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+PickerSubscriptionStoreControlStyle+PickerSubscriptionStoreControlStyle.swift @@ -0,0 +1,14 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +@available(iOS 13.0, macOS 10.15, watchOS 7.0, *) +@available(tvOS, unavailable) +public extension ISubscriptionControlStyle where Self == PickerSubscriptionStoreControlStyle { + static var picker: PickerSubscriptionStoreControlStyle { + PickerSubscriptionStoreControlStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+ProminentPickerSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+ProminentPickerSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..0558d728f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Extensions/ISubscriptionControlStyle+ProminentPickerSubscriptionStoreControlStyle.swift @@ -0,0 +1,16 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +public extension ISubscriptionControlStyle where Self == ProminentPickerSubscriptionStoreControlStyle { + static var prominentPicker: ProminentPickerSubscriptionStoreControlStyle { + ProminentPickerSubscriptionStoreControlStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Protocols/ISubscriptionControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Protocols/ISubscriptionControlStyle.swift new file mode 100644 index 000000000..38ba52506 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/Protocols/ISubscriptionControlStyle.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +public protocol ISubscriptionControlStyle { + /// A view that represents the body of an in-app subscription store control. + associatedtype Body: View + + /// The properties of an in-app subscription store control. + typealias Configuration = SubscriptionStoreControlStyleConfiguration + + /// Creates a view that represents the body of an in-app subscription store control. + /// + /// - Parameter configuration: The properties of an in-app subscription store control. + @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/AutomaticSubscriptionControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/AutomaticSubscriptionControlStyle.swift new file mode 100644 index 000000000..e702ce0bb --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/AutomaticSubscriptionControlStyle.swift @@ -0,0 +1,30 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - AutomaticSubscriptionControlStyle + +@available(watchOS, unavailable) +struct AutomaticSubscriptionControlStyle: ISubscriptionControlStyle { + // MARK: ISubscriptionControlStyle + + func makeBody(configuration: Configuration) -> some View { + #if os(iOS) + return ProminentPickerSubscriptionStoreControlStyle().makeBody(configuration: configuration) + #else + return ButtonSubscriptionStoreControlStyle().makeBody(configuration: configuration) + #endif + } +} + +// MARK: - Extensions + +@available(watchOS, unavailable) +extension ISubscriptionControlStyle where Self == AutomaticSubscriptionControlStyle { + static var automatic: AutomaticSubscriptionControlStyle { + AutomaticSubscriptionControlStyle() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..ac0b0825d --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle.swift @@ -0,0 +1,43 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - BorderedSubscriptionStoreControlStyle + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct BorderedSubscriptionStoreControlStyle: ISubscriptionControlStyle { + // MARK: Properties + + init() {} + + // MARK: ISubscriptionControlStyle + + func makeBody(configuration: Configuration) -> some View { + BorderedSubscriptionStoreControlStyleView(configuration: configuration) + } +} + +// MARK: - Preview + +#if swift(>=5.9) && os(iOS) + #Preview { + VStack { + BorderedSubscriptionStoreControlStyle().makeBody( + configuration: .init( + label: .init(Text("Name")), + description: .init(Text("Name")), + price: .init(Text("Name")), + isSelected: true, + isActive: true, + action: {} + ) + ) + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyleView.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyleView.swift new file mode 100644 index 000000000..4a6095373 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyle/BorderedSubscriptionStoreControlStyleView.swift @@ -0,0 +1,96 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - BorderedSubscriptionStoreControlStyleView + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +// swiftlint:disable:next type_name +struct BorderedSubscriptionStoreControlStyleView: View { + // MARK: Properties + + @Environment(\.subscriptionStoreButtonLabel) private var subscriptionStoreButtonLabel + @Environment(\.tintColor) private var tintColor + + private let configuration: ISubscriptionControlStyle.Configuration + + // MARK: Initialization + + init(configuration: ISubscriptionControlStyle.Configuration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + Button(action: { + configuration.trigger() + }, label: { + labelView(configuration) + }) + .buttonStyle(PrimaryButtonStyle()) + } + + // MARK: Private + + @ViewBuilder + private func labelView(_ configuration: ISubscriptionControlStyle.Configuration) -> some View { + switch subscriptionStoreButtonLabel { + case .action: + textView + case .displayName: + configuration.label + .font(.body.weight(.bold)) + .contrast(tintColor) + case .multiline: + VStack { + configuration.label + .font(.body.weight(.bold)) + .contrast(tintColor) + + if configuration.isActive { + Text(L10n.Common.Subscription.Status.yourCurrentPlan) + .font(.footnote.weight(.medium)) + .contrast(tintColor) + } else { + configuration.price + .font(.footnote.weight(.medium)) + .contrast(tintColor) + } + } + case .price: + configuration.price + .font(.footnote.weight(.medium)) + } + } + + private var textView: some View { + VStack { + Text(L10n.Common.Subscription.Action.subscribe) + .font(.body) + .fontWeight(.bold) + .contrast(tintColor) + } + } +} + +#if swift(>=5.9) && os(iOS) + #Preview { + BorderedSubscriptionStoreControlStyleView( + configuration: .init( + label: .init(Text("Name")), + description: .init(Text("Name")), + price: .init(Text("Name")), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..712ff7af9 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle/ButtonSubscriptionStoreControlStyle.swift @@ -0,0 +1,43 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ButtonSubscriptionStoreControlStyle + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +@available(watchOS, unavailable) +public struct ButtonSubscriptionStoreControlStyle: ISubscriptionControlStyle { + // MARK: Initialization + + public init() {} + + // MARK: ISubscriptionControlStyle + + public func makeBody(configuration: Configuration) -> some View { + #if os(tvOS) + return CardButtonSubscriptionStoreControlStyle().makeBody(configuration: configuration) + #else + return BorderedSubscriptionStoreControlStyle().makeBody(configuration: configuration) + #endif + } +} + +// MARK: - Preview + +#if swift(>=5.9) && os(iOS) + #Preview { + ButtonSubscriptionStoreControlStyle().makeBody( + configuration: .init( + label: .init(Text("Name")), + description: .init(Text("Name")), + price: .init(Text("Name")), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..a99b93049 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle.swift @@ -0,0 +1,55 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +#if os(tvOS) + @available(tvOS 13.0, *) + @available(iOS, unavailable) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(visionOS, unavailable) + struct CardButtonSubscriptionStoreControlStyle: ISubscriptionControlStyle { + // MARK: ISubscriptionControlStyle + + func makeBody(configuration: Configuration) -> some View { + if #available(tvOS 15.0, *) { + contentView(configuration: configuration) + .buttonStyle(CardButtonStyle()) + } else { + contentView(configuration: configuration) + } + } + + // MARK: Private + + private func contentView(configuration: Configuration) -> some View { + Button( + action: { + configuration.trigger() + }, label: { + CardButtonSubscriptionStoreControlView(configuration: configuration) + } + ) + } + } +#endif + +// MARK: - Preview + +#if swift(>=5.9) && os(tvOS) + #Preview { + CardButtonSubscriptionStoreControlStyle().makeBody( + configuration: .init( + label: .init(Text("Name")), + description: .init(Text("Name")), + price: .init(Text("Name")), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlView.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlView.swift new file mode 100644 index 000000000..b3fe658de --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlStyle/CardButtonSubscriptionStoreControlView.swift @@ -0,0 +1,98 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - CardButtonSubscriptionStoreControlView + +@available(tvOS 13.0, *) +@available(iOS, unavailable) +@available(macOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct CardButtonSubscriptionStoreControlView: View { + // MARK: Properties + +// @Environment(\.isFocused) private var isFocused: Bool + @Environment(\.tintColor) private var tintColor + + private let configuration: SubscriptionStoreControlStyleConfiguration + + // MARK: Initialization + + init(configuration: SubscriptionStoreControlStyleConfiguration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + ZStack { + Rectangle() + .fill(tintColor) // isFocused ? tintColor.opacity(0.85) : tintColor) + + VStack(alignment: .leading) { + VStack(alignment: .leading) { + if configuration.isActive { + planView + } + + configuration.label + .contrast(tintColor) + .font(.headline) + configuration.price + .contrast(tintColor) + .font(.caption.weight(.medium)) + .layoutPriority(1) + } + + Spacer(minLength: .zero) + .frame(maxWidth: .infinity) + + configuration.description + .contrast(tintColor) + .font(.footnote) + } + .padding() + } + .frame(minWidth: .minWidth, minHeight: .minHeight) + .fixedSize(horizontal: true, vertical: true) + } + + // MARK: Private + + private var planView: some View { + HStack { + Text(L10n.Common.Subscription.Status.yourPlan) + .opacity(0.8) + .contrast(tintColor) + .font(.caption) + } + } +} + +// MARK: - Constants + +private extension CGFloat { + static let minWidth = 528.0 + static let minHeight = 204.0 +} + +// MARK: - Preview + +#if swift(>=5.9) && os(tvOS) + #Preview { + CardButtonSubscriptionStoreControlView( + configuration: .init( + label: .init(Text("Name")), + description: .init(Text("Name")), + price: .init(Text("Name")), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..774872a00 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle.swift @@ -0,0 +1,40 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PickerSubscriptionStoreControlStyle + +@available(iOS 13.0, macOS 10.15, watchOS 7.0, *) +@available(tvOS, unavailable) +@available(visionOS, unavailable) +public struct PickerSubscriptionStoreControlStyle: ISubscriptionControlStyle { + // MARK: Initialization + + public init() {} + + // MARK: ISubscriptionControlStyle + + public func makeBody(configuration: Configuration) -> some View { + PickerSubscriptionStoreControlStyleView(configuration: configuration) + } +} + +// MARK: - Preview + +#if swift(>=5.9) && os(iOS) + #Preview { + PickerSubscriptionStoreControlStyle().makeBody( + configuration: .init( + label: .init(Text("Name").eraseToAnyView()), + description: .init(Text("Name").eraseToAnyView()), + price: .init(Text("Name").eraseToAnyView()), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyleView.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyleView.swift new file mode 100644 index 000000000..758e4e8cb --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyle/PickerSubscriptionStoreControlStyleView.swift @@ -0,0 +1,138 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PickerSubscriptionStoreControlStyleView + +@available(iOS 13.0, macOS 10.15, watchOS 7.0, *) +@available(tvOS, unavailable) +@available(visionOS, unavailable) +struct PickerSubscriptionStoreControlStyleView: View { + // MARK: Properties + + @Environment(\.subscriptionPickerItemBackground) private var background + @Environment(\.subscriptionViewTint) private var tintColor + + private let configuration: ISubscriptionControlStyle.Configuration + + // MARK: Initialization + + init(configuration: ISubscriptionControlStyle.Configuration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + contentView + #if os(iOS) || os(macOS) || os(watchOS) + .onTapGesture { + configuration.trigger() + } + #endif + } + + // MARK: Private + + private var contentView: some View { + VStack(alignment: .leading, spacing: .spacing10px) { + HStack { + VStack(alignment: .leading, spacing: 8.0) { + if configuration.isActive { + planView + } + configuration.label + .font(.headline) + } + Spacer() + checkmarkView + } + + configuration.price + .font(.subheadline) + separatorView + configuration.description + .font(.subheadline) + } + .padding() + .background(background) + .mask(rectangleBackground) + } + + private var checkmarkView: some View { + ImageView( + systemName: configuration.isSelected ? .checkmark : .circle, + defaultImage: configuration.isSelected ? Media.Media.checkmark.swiftUIImage : Media.Media.circle.swiftUIImage + ) + .foregroundColor(configuration.isSelected ? tintColor : Palette.systemGray2.opacity(0.7)) + .frame( + width: CGSize.iconSize.width, + height: CGSize.iconSize.height + ) + .background(configuration.isSelected ? Color.white : .clear) + .mask(Circle()) + } + + private var separatorView: some View { + Rectangle() + .foregroundColor(Palette.systemGray4) + .frame(maxWidth: .infinity) + .frame(height: .separatorHeight) + } + + private var rectangleBackground: RoundedRectangle { + RoundedRectangle(cornerSize: .cornerSize) + } + + private var planView: some View { + HStack(spacing: 4.0) { + ImageView(systemName: .star, defaultImage: Media.Media.star.swiftUIImage) + .frame( + width: CGSize.planImageSize.width, + height: CGSize.planImageSize.height + ) + .foregroundColor(tintColor) + Text(L10n.Common.Subscription.Status.yourPlan.uppercased()) + .font(.caption.weight(.bold)) + .foregroundColor(Palette.systemGray) + } + } +} + +// MARK: - Preview + +#if swift(>=5.9) && os(iOS) + #Preview { + PickerSubscriptionStoreControlStyleView( + configuration: .init( + label: .init(Text("Name").eraseToAnyView()), + description: .init(Text("Name").eraseToAnyView()), + price: .init(Text("Name").eraseToAnyView()), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif + +// MARK: - Constants + +private extension String { + static let checkmark = "checkmark.circle.fill" + static let circle = "circle" + static let star = "star" +} + +private extension CGSize { + static let cornerSize = CGSize(width: 18, height: 18) + static let iconSize = CGSize(width: 26, height: 26) + static let planImageSize = CGSize(width: 14, height: 14) +} + +private extension CGFloat { + static let separatorHeight = 1.0 +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle.swift new file mode 100644 index 000000000..a588b2a4f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle.swift @@ -0,0 +1,42 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProminentPickerSubscriptionStoreControlStyle + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +// swiftlint:disable:next type_name +public struct ProminentPickerSubscriptionStoreControlStyle: ISubscriptionControlStyle { + // MARK: Initialization + + public init() {} + + // MARK: ISubscriptionControlStyle + + public func makeBody(configuration: Configuration) -> some View { + ProminentPickerSubscriptionStoreControlStyleView(configuration: configuration) + } +} + +// MARK: - Preview + +#if swift(>=5.9) && os(iOS) + #Preview { + ProminentPickerSubscriptionStoreControlStyle().makeBody( + configuration: .init( + label: .init(Text("Name").eraseToAnyView()), + description: .init(Text("Name").eraseToAnyView()), + price: .init(Text("Name").eraseToAnyView()), + isSelected: true, + isActive: true, + action: {} + ) + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyleView.swift b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyleView.swift new file mode 100644 index 000000000..5d7c87846 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Styles/Subscription/SubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyle/ProminentPickerSubscriptionStoreControlStyleView.swift @@ -0,0 +1,52 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProminentPickerSubscriptionStoreControlStyleView + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +// swiftlint:disable:next type_name +struct ProminentPickerSubscriptionStoreControlStyleView: View { + // MARK: Properties + + @Environment(\.subscriptionViewTint) private var tintColor + + private let configuration: ISubscriptionControlStyle.Configuration + + // MARK: Initialization + + init(configuration: ISubscriptionControlStyle.Configuration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + PickerSubscriptionStoreControlStyle().makeBody(configuration: configuration) + .overlay(overlayView(configuration: configuration)) + } + + // MARK: Private + + private var rectangleBackground: RoundedRectangle { + RoundedRectangle(cornerSize: .cornerSize) + } + + private func overlayView(configuration: ISubscriptionControlStyle.Configuration) -> some View { + rectangleBackground + .strokeBorder(tintColor, lineWidth: 2) + .opacity((configuration.isSelected) ? 1.0 : .zero) + } +} + +// MARK: - Constants + +private extension CGSize { + static let cornerSize = CGSize(width: 18, height: 18) +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ActivityIndicatorModifier.swift b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ActivityIndicatorModifier.swift new file mode 100644 index 000000000..2e017bf92 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ActivityIndicatorModifier.swift @@ -0,0 +1,42 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ActivityIndicatorModifier + +struct ActivityIndicatorModifier: ViewModifier { + // MARK: Properties + + private let isLoading: Bool + + // MARK: Initialization + + init(isLoading: Bool) { + self.isLoading = isLoading + } + + // MARK: ViewModifier + + func body(content: Content) -> some View { + if isLoading { + ZStack(alignment: .center) { + content + .disabled(isLoading) + .blur(radius: self.isLoading ? 3 : 0) + + LoadingView(type: .backgrouned, message: "Purchasing the subscription...") + } + } else { + content + } + } +} + +extension View { + func activityIndicator(isLoading: Bool) -> some View { + modifier(ActivityIndicatorModifier(isLoading: isLoading)) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/BlurEffectModifier.swift b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/BlurEffectModifier.swift new file mode 100644 index 000000000..a043c09dd --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/BlurEffectModifier.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +#if os(iOS) || os(tvOS) + struct BlurEffectModifier: ViewModifier { + init() {} + + func body(content: Content) -> some View { + content + .overlay(BlurVisualEffectView()) + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ErrorAlertViewModifier.swift b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ErrorAlertViewModifier.swift new file mode 100644 index 000000000..d922ce432 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/ErrorAlertViewModifier.swift @@ -0,0 +1,47 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ErrorAlertViewModifier + +/// A view modifier that handles an error alert. +struct ErrorAlertViewModifier: ViewModifier { + // MARK: Properties + + /// An error to be presented. + @Binding private var error: Error? + + /// A binding to control the presentation state of the error. + private var isErrorPresented: Binding { + Binding { error != nil } set: { _ in error = nil } + } + + // MARK: Initialization + + /// Creates an ``ErrorAlertViewModifier`` instance. + /// + /// - Parameter error: A binding to control the presentation state of the error. + init(error: Binding) { + _error = error + } + + // MARK: ViewModifier + + func body(content: Content) -> some View { + content + .alert(isPresented: isErrorPresented) { + Alert(title: Text(L10n.Error.Default.title), message: Text(error?.localizedDescription ?? "")) + } + } +} + +// MARK: - Extensions + +extension View { + func errorAlert(_ error: Binding) -> some View { + modifier(ErrorAlertViewModifier(error: error)) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/LoadViewModifier.swift b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/LoadViewModifier.swift new file mode 100644 index 000000000..bf095ecd6 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/LoadViewModifier.swift @@ -0,0 +1,47 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - LoadViewModifier + +/// A view modifier that triggers a handler once when view is loaded. +struct LoadViewModifier: ViewModifier { + // MARK: Private + + /// A Bool value that indicates the view is loaded. + @State private var isLoaded = false + + /// A handler closure. + private let handler: () -> Void + + // MARK: Initialization + + /// Creates a ``LoadViewModifier`` instance. + /// + /// - Parameter handler: A handler closure to be performed when view is loaded. + init(handler: @escaping () -> Void) { + self.handler = handler + } + + // MARK: ViewModifier + + func body(content: Content) -> some View { + content.onAppear { + if !isLoaded { + handler() + isLoaded.toggle() + } + } + } +} + +// MARK: - Extensions + +extension View { + func onLoad(_ handler: @escaping () -> Void) -> some View { + modifier(LoadViewModifier(handler: handler)) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/PaywallViewModifier.swift b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/PaywallViewModifier.swift new file mode 100644 index 000000000..3bdfbc8ee --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/ViewModifiers/PaywallViewModifier.swift @@ -0,0 +1,44 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +/// A view modifier provides a paywall functionality. +@available(watchOS, unavailable) +struct PaywallViewModifier: ViewModifier { + // MARK: Properties + + /// The paywall type. + private let paywallType: PaywallType + /// The binding to control the presentation state of the paywall. + private let presented: Binding + + // MARK: Initialization + + /// Creates a `PaywallViewModifier` instance. + /// + /// - Parameters: + /// - paywallType: The paywall type. + /// - presented: The binding to control the presentation state of the paywall. + init( + paywallType: PaywallType, + presented: Binding + ) { + self.paywallType = paywallType + self.presented = presented + } + + // MARK: ViewModifier + + func body(content: Content) -> some View { + content + .sheet(isPresented: presented) { + ZStack { + Palette.systemBackground.edgesIgnoringSafeArea(.all) + PaywallView(paywallType: paywallType) + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Views/ActivityIndicator/ActivityIndicatorView.swift b/Sources/FlareUI/Classes/Presentation/Components/Views/ActivityIndicator/ActivityIndicatorView.swift new file mode 100644 index 000000000..9ba4db625 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Views/ActivityIndicator/ActivityIndicatorView.swift @@ -0,0 +1,69 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +#if os(iOS) || os(tvOS) + typealias ViewRepresentable = UIViewRepresentable +#elseif os(macOS) + typealias ViewRepresentable = NSViewRepresentable +#elseif os(watchOS) + typealias ViewRepresentable = WKInterfaceObjectRepresentable +#endif + +// MARK: - ActivityIndicatorView + +#if os(iOS) || os(tvOS) || os(macOS) + struct ActivityIndicatorView: ViewRepresentable { + // MARK: Properties + + @Binding var isAnimating: Bool + + #if os(iOS) || os(tvOS) + let style: UIActivityIndicatorView.Style + #endif + + // MARK: UIViewRepresentable + + #if os(macOS) + func makeNSView(context _: Context) -> NSProgressIndicator { + let progressIndicator = NSProgressIndicator() + progressIndicator.style = .spinning + progressIndicator.usesThreadedAnimation = true + return progressIndicator + } + + func updateNSView(_ nsView: NSViewType, context _: Context) { + if isAnimating { + nsView.startAnimation(nil) + } else { + nsView.stopAnimation(nil) + } + } + #endif + + #if os(iOS) || os(tvOS) + func makeUIView(context _: UIViewRepresentableContext) -> UIActivityIndicatorView { + UIActivityIndicatorView(style: style) + } + + func updateUIView( + _ uiView: UIActivityIndicatorView, + context _: UIViewRepresentableContext + ) { + if isAnimating { + uiView.startAnimating() + } else { + uiView.stopAnimating() + } + } + #endif + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Views/BlurVisualEffectView/BlurVisualEffectView.swift b/Sources/FlareUI/Classes/Presentation/Components/Views/BlurVisualEffectView/BlurVisualEffectView.swift new file mode 100644 index 000000000..706958829 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Views/BlurVisualEffectView/BlurVisualEffectView.swift @@ -0,0 +1,27 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - BlurVisualEffectView + +#if os(iOS) || os(tvOS) + struct BlurVisualEffectView: UIViewRepresentable { + func makeUIView(context: Context) -> UIVisualEffectView { + UIVisualEffectView(effect: UIBlurEffect(style: context.environment.blurEffectStyle)) + } + + func updateUIView(_ uiView: UIVisualEffectView, context: Context) { + uiView.effect = UIBlurEffect(style: context.environment.blurEffectStyle) + } + } + + extension View { + /// Creates a blur effect. + func blurEffect() -> some View { + ModifiedContent(content: self, modifier: BlurEffectModifier()) + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Components/Views/ImageView/ImageView.swift b/Sources/FlareUI/Classes/Presentation/Components/Views/ImageView/ImageView.swift new file mode 100644 index 000000000..31dbbb352 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Views/ImageView/ImageView.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct ImageView: View { + // MARK: Properties + + private let systemName: String + private let defaultImage: Image + + // MARK: Initialization + + init(systemName: String, defaultImage: Image) { + self.systemName = systemName + self.defaultImage = defaultImage + } + + // MARK: View + + var body: some View { + Group { + if #available(macOS 11.0, iOS 14.0, tvOS 14.0, *) { + Image(systemName: systemName) + .resizable() + } else { + defaultImage + .resizable() + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Views/ProductPlaceholderView/ProductPlaceholderView.swift b/Sources/FlareUI/Classes/Presentation/Components/Views/ProductPlaceholderView/ProductPlaceholderView.swift new file mode 100644 index 000000000..837d41e1e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Views/ProductPlaceholderView/ProductPlaceholderView.swift @@ -0,0 +1,185 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProductPlaceholderView + +struct ProductPlaceholderView: View { + // MARK: Types + + enum Style { + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + case large + case compact + } + + // MARK: Properties + + private let isIconHidden: Bool + private let style: Style + + // MARK: Initialization + + init(isIconHidden: Bool, style: Style) { + self.isIconHidden = isIconHidden + self.style = style + } + + // MARK: View + + var body: some View { + contentView + } + + // MARK: Private + + private var contentView: some View { + stackView(spacing: .iconSpacing) { + if !isIconHidden { + iconView + } + stackView(spacing: 8.0) { + textView + Spacer() + buttonView + } + .padding(.padding) + } + .background(value(default: Color.clear, tvOS: Palette.dynamicBackground)) + .frame(height: style == .compact ? metrics(compact: .height, large: nil) : nil) + .frame(maxHeight: metrics(compact: .height, large: .largeHeight)) + .fixedSize(horizontal: false, vertical: true) + .padding(.vertical, value(default: .spacing, tvOS: .zero)) + } + + private var textView: some View { + VStack( + alignment: metrics(compact: .leading, large: .center), + spacing: .spacing + ) { + Palette.gray + .frame( + idealWidth: .titleWidth, + maxWidth: .titleWidth, + idealHeight: .titleHeight, + maxHeight: .titleHeight + ) + .mask(RoundedRectangle(cornerRadius: .cornerRadius)) + Palette.gray + .frame( + idealWidth: .descriptionWidth, + maxWidth: .descriptionWidth, + idealHeight: .descriptionHeight, + maxHeight: .descriptionHeight + ) + .mask(RoundedRectangle(cornerRadius: .cornerRadius)) + #if os(tvOS) + Spacer() + #endif + } + } + + private var buttonView: some View { + Palette.gray + .frame( + idealWidth: .buttonWidth, + maxWidth: .buttonWidth, + idealHeight: .buttonHeight, + maxHeight: .buttonHeight + ) + .mask(buttonMask) + } + + private var iconView: some View { + Palette.gray + .frame( + idealWidth: metrics(compact: .iconWidth, large: .iconLargeWidth), + maxWidth: metrics(compact: .iconWidth, large: .iconLargeWidth), + idealHeight: metrics(compact: .iconHeight, large: .iconLargeHeight), + maxHeight: metrics(compact: .iconHeight, large: .iconLargeHeight) + ) + } + + private func metrics(compact: T, large: T?) -> T { + #if os(iOS) + switch style { + case .compact: + return compact + case .large: + return large ?? compact + } + #else + return compact + #endif + } + + private var buttonMask: some View { + #if os(tvOS) + RoundedRectangle(cornerRadius: .zero) + #else + Capsule() + #endif + } + + @ViewBuilder + private func stackView(spacing: CGFloat = .zero, @ViewBuilder content: () -> some View) -> some View { + #if os(iOS) + Group { + switch style { + case .large: + VStack(spacing: spacing) { + content() + } + case .compact: + HStack(spacing: spacing) { + content() + } + } + } + #else + HStack(spacing: spacing) { + content() + } + #endif + } +} + +// MARK: Preview + +#if swift(>=5.9) + #Preview { + Group { + ForEach(0 ..< 10) { _ in + ProductPlaceholderView(isIconHidden: true, style: .compact) + ProductPlaceholderView(isIconHidden: false, style: .compact) + } + } + } +#endif + +// MARK: Constants + +private extension CGFloat { + static let padding = value(default: .zero, tvOS: 24.0) + static let spacing = value(default: 2.0, tvOS: 10.0) + static let height = value(default: 60.0, tvOS: 200.0) + static let largeHeight = value(default: 200.0) + static let titleWidth = value(default: 123.0, tvOS: 240.0) + static let titleHeight = value(default: 20.0, tvOS: 30.0) + static let descriptionWidth = value(default: 208.0, tvOS: 180.0) + static let descriptionHeight = value(default: 14.0, tvOS: 24.0) + static let buttonWidth = value(default: 76.0, tvOS: 90.0) + static let buttonHeight = value(default: 34.0, tvOS: 25) + static let cornerRadius = value(default: CGFloat.cornerRadius4px, tvOS: .zero) + static let iconWidth = value(default: 60.0, tvOS: 330.0) + static let iconHeight = value(default: 60.0, tvOS: 200.0) + static let iconLargeWidth = value(default: 105.0) + static let iconLargeHeight = value(default: 105.0) + static let iconSpacing = value(default: 8.0, tvOS: .zero) +} diff --git a/Sources/FlareUI/Classes/Presentation/Components/Views/SafariWebView/SafariWebView.swift b/Sources/FlareUI/Classes/Presentation/Components/Views/SafariWebView/SafariWebView.swift new file mode 100644 index 000000000..b598e0bf2 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Components/Views/SafariWebView/SafariWebView.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if os(iOS) + import SafariServices + import SwiftUI + + struct SafariWebView: UIViewControllerRepresentable { + let url: URL + + func makeUIViewController(context _: Context) -> SFSafariViewController { + SFSafariViewController(url: url) + } + + func updateUIViewController(_: SFSafariViewController, context _: Context) {} + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Helpers/ViewWrapper.swift b/Sources/FlareUI/Classes/Presentation/Helpers/ViewWrapper.swift new file mode 100644 index 000000000..c25f52115 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Helpers/ViewWrapper.swift @@ -0,0 +1,38 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - IViewWrapper + +/// A type defines a wrapper for a view. +protocol IViewWrapper: View { + associatedtype ViewModel + + /// Creates a new `IViewWrapper` instance. + /// + /// - Parameter viewModel: The view model. + init(viewModel: ViewModel) +} + +// MARK: - ViewWrapper + +struct ViewWrapper: View where ViewWrapper.ViewModel == ViewModel { + // MARK: Properties + + @ObservedObject private var viewModel: WrapperViewModel + + // MARK: Initialization + + init(viewModel: WrapperViewModel) { + self.viewModel = viewModel + } + + // MARK: IViewWrapper + + var body: some View { + ViewWrapper(viewModel: viewModel.model) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Helpers/WrapperViewModel.swift b/Sources/FlareUI/Classes/Presentation/Helpers/WrapperViewModel.swift new file mode 100644 index 000000000..48aee03f1 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Helpers/WrapperViewModel.swift @@ -0,0 +1,23 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +/// An observable view model. +final class WrapperViewModel: ObservableObject { + // MARK: Properties + + /// The model object. + @Published var model: T + + // MARK: Initialization + + /// Creates a `ViewModel` instance. + /// + /// - Parameter model: The model object. + init(model: T) { + self.model = model + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PaywallView/PaywallView.swift b/Sources/FlareUI/Classes/Presentation/Views/PaywallView/PaywallView.swift new file mode 100644 index 000000000..120f5ddb4 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PaywallView/PaywallView.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +struct PaywallView: View { + // MARK: Properties + + private let presentationAssembly: IPresentationAssembly + private let paywallType: PaywallType + + // MARK: Initialization + + init( + paywallType: PaywallType, + presentationAssembly: IPresentationAssembly = PresentationAssembly() + ) { + self.paywallType = paywallType + self.presentationAssembly = presentationAssembly + } + + // MARK: View + + var body: some View { + switch paywallType { + case let .subscriptions(productIDs): + let productIDs: any Collection = productIDs + presentationAssembly.subscritpionsViewAssembly.assemble(ids: productIDs) + case let .products(productIDs): + let productIDs: any Collection = productIDs + presentationAssembly.productsViewAssembly.assemble(ids: productIDs) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonAssembly.swift new file mode 100644 index 000000000..cac5bba4d --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonAssembly.swift @@ -0,0 +1,22 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - IPoliciesButtonAssembly + +@available(watchOS, unavailable) +protocol IPoliciesButtonAssembly { + func assemble() -> PoliciesButtonView +} + +// MARK: - PoliciesButtonAssembly + +@available(watchOS, unavailable) +final class PoliciesButtonAssembly: IPoliciesButtonAssembly { + func assemble() -> PoliciesButtonView { + PoliciesButtonView() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonView.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonView.swift new file mode 100644 index 000000000..d17073248 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/PoliciesButtonView.swift @@ -0,0 +1,120 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PoliciesButtonView + +@available(watchOS, unavailable) +struct PoliciesButtonView: View { + // MARK: Types + + private enum LinkType { + case termsOfService + case privacyPolicy + } + + // MARK: Private + + @State private var link: LinkType? + private var isPresented: Binding { + Binding { link != nil } set: { _ in link = nil } + } + + @Environment(\.subscriptionTermsOfServiceURL) private var subscriptionTermsOfServiceURL + @Environment(\.subscriptionPrivacyPolicyURL) private var subscriptionPrivacyPolicyURL + @Environment(\.subscriptionTermsOfServiceDestination) private var subscriptionTermsOfServiceDestination + @Environment(\.subscriptionPrivacyPolicyDestination) private var subscriptionPrivacyPolicyDestination + + @Environment(\.policiesButtonStyle) private var policiesButtonStyle + + // MARK: View + + var body: some View { + policiesButtonStyle.makeBody( + configuration: .init( + termsOfUseView: .init(termsOfServiceButton), + privacyPolicyView: .init(privacyPolicyButton) + ) + ) + .font(.footnote) + .sheet(isPresented: isPresented) { + contentView + } + } + + // MARK: Private + + private var termsOfServiceButton: some View { + Button(action: { + link = .termsOfService + }, label: { + Text(L10n.Common.termsOfService) + }) + } + + private var privacyPolicyButton: some View { + Button(action: { + link = .privacyPolicy + }, label: { + Text(L10n.Common.privacyPolicy) + }) + } + + private var contentView: some View { + link.map { link in + Group { + switch link { + case .privacyPolicy: + privacyPolicyContentView + case .termsOfService: + privacyTermsOfServiceView + } + } + } + } + + @ViewBuilder + private var privacyPolicyContentView: some View { + if subscriptionPrivacyPolicyDestination != nil { + subscriptionPrivacyPolicyDestination + } else if let subscriptionPrivacyPolicyURL { + #if os(iOS) + safariView(subscriptionPrivacyPolicyURL) + #endif + } else { + PoliciesUnavailableView(type: .privacyPolicy) + } + } + + @ViewBuilder + private var privacyTermsOfServiceView: some View { + if subscriptionTermsOfServiceDestination != nil { + subscriptionTermsOfServiceDestination + } else if let subscriptionTermsOfServiceURL { + #if os(iOS) + safariView(subscriptionTermsOfServiceURL) + #endif + } else { + PoliciesUnavailableView(type: .termsOfService) + } + } + + #if os(iOS) + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + private func safariView(_ url: URL) -> some View { + SafariWebView(url: url).edgesIgnoringSafeArea(.all) + } + #endif +} + +#if swift(>=5.9) && os(iOS) + #Preview { + PoliciesButtonView() + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AnyPoliciesButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AnyPoliciesButtonStyle.swift new file mode 100644 index 000000000..8b1d20ab0 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AnyPoliciesButtonStyle.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct AnyPoliciesButtonStyle: IPoliciesButtonStyle { + // MARK: Properties + + let style: any IPoliciesButtonStyle + + /// A private property to hold the closure that creates the body of the view + private var _makeBody: (Configuration) -> AnyView + + // MARK: Initialization + + /// Initializes the `AnyPoliciesButtonStyle` with a specific style conforming to `IPoliciesButtonStyle`. + /// + /// - Parameter style: A product style. + init(style: S) { + self.style = style + _makeBody = { configuration in + AnyView(style.makeBody(configuration: configuration)) + } + } + + // MARK: IPoliciesButtonStyle + + /// Implements the makeBody method required by `IPoliciesButtonStyle`. + func makeBody(configuration: Configuration) -> some View { + _makeBody(configuration) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AutomaticPoliciesButtonStyle/AutomaticPoliciesButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AutomaticPoliciesButtonStyle/AutomaticPoliciesButtonStyle.swift new file mode 100644 index 000000000..6e876282e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/AutomaticPoliciesButtonStyle/AutomaticPoliciesButtonStyle.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +struct AutomaticPoliciesButtonStyle: IPoliciesButtonStyle { + func makeBody(configuration: Configuration) -> some View { + #if os(tvOS) + return TVPoliciesButtonStyle().makeBody(configuration: configuration) + #else + return DefaultPoliciesButtonStyle().makeBody(configuration: configuration) + #endif + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/Configuration/PoliciesButtonStyleConfiguration.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/Configuration/PoliciesButtonStyleConfiguration.swift new file mode 100644 index 000000000..3cccb72a4 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/Configuration/PoliciesButtonStyleConfiguration.swift @@ -0,0 +1,23 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct PoliciesButtonStyleConfiguration { + // MARK: Types + + struct ButtonView: View { + var body: AnyView + + init(_ view: Content) { + body = view.eraseToAnyView() + } + } + + // MARK: Properties + + let termsOfUseView: ButtonView + let privacyPolicyView: ButtonView +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyle.swift new file mode 100644 index 000000000..a76af39f4 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyle.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13.0, macOS 10.15, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +struct DefaultPoliciesButtonStyle: IPoliciesButtonStyle { + func makeBody(configuration: Configuration) -> some View { + DefaultPoliciesButtonStyleView(configuration: configuration) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyleView.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyleView.swift new file mode 100644 index 000000000..6bd003ee9 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/DefaultPoliciesButtonStyle/DefaultPoliciesButtonStyleView.swift @@ -0,0 +1,42 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - DefaultPoliciesButtonStyleView + +struct DefaultPoliciesButtonStyleView: View { + // MARK: Properties + + private let configuration: PoliciesButtonStyleConfiguration + + @Environment(\.tintColor) private var tintColor + + // MARK: Initialization + + init(configuration: PoliciesButtonStyleConfiguration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + HStack(spacing: .spacing) { + configuration.termsOfUseView + .foregroundColor(tintColor) + + Text(L10n.Common.Words.and) + + configuration.privacyPolicyView + .foregroundColor(tintColor) + } + } +} + +// MARK: - Constants + +private extension CGFloat { + static let spacing = 3.0 +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/IPoliciesButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/IPoliciesButtonStyle.swift new file mode 100644 index 000000000..bd14daf07 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/IPoliciesButtonStyle.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +protocol IPoliciesButtonStyle { + /// A view that represents the body of an in-app subscription store control. + associatedtype Body: View + + /// The properties of an in-app subscription store control. + typealias Configuration = PoliciesButtonStyleConfiguration + + /// Creates a view that represents the body of an in-app subscription store control. + /// + /// - Parameter configuration: The properties of an in-app subscription store control. + @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/TVPoliciesButtonStyle/TVPoliciesButtonStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/TVPoliciesButtonStyle/TVPoliciesButtonStyle.swift new file mode 100644 index 000000000..15208de85 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Styles/TVPoliciesButtonStyle/TVPoliciesButtonStyle.swift @@ -0,0 +1,27 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - TVPoliciesButtonStyle + +@available(tvOS 13.0, *) +@available(macOS, unavailable) +@available(watchOS, unavailable) +@available(iOS, unavailable) +struct TVPoliciesButtonStyle: IPoliciesButtonStyle { + func makeBody(configuration: Configuration) -> some View { + HStack(spacing: .spacing) { + configuration.termsOfUseView + configuration.privacyPolicyView + } + } +} + +// MARK: - Constants + +private extension CGFloat { + static let spacing = 60.0 +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Views/PoliciesUnavailableView.swift b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Views/PoliciesUnavailableView.swift new file mode 100644 index 000000000..fc32bd885 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/PoliciesButtonAssembly/Views/PoliciesUnavailableView.swift @@ -0,0 +1,66 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - PoliciesUnavailableView + +struct PoliciesUnavailableView: View { + // MARK: Types + + enum PolicyType { + case privacyPolicy + case termsOfService + + var title: String { + switch self { + case .privacyPolicy: + return L10n.Policies.Unavailable.PrivacyPolicy.title + case .termsOfService: + return L10n.Policies.Unavailable.TermsOfService.title + } + } + + var message: String { + switch self { + case .privacyPolicy: + return L10n.Policies.Unavailable.PrivacyPolicy.message + case .termsOfService: + return L10n.Policies.Unavailable.TermsOfService.message + } + } + } + + // MARK: Properties + + private let type: PolicyType + + // MARK: Initialization + + init(type: PolicyType) { + self.type = type + } + + // MARK: View + + var body: some View { + VStack { + Text(type.title) + .font(.title) + .multilineTextAlignment(.center) + Text(type.message) + .font(.body) + .multilineTextAlignment(.center) + .foregroundColor(Palette.systemGray) + } + .padding() + } +} + +#if swift(>=5.9) + #Preview { + StoreUnavaliableView(productType: .product) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPresenter.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPresenter.swift new file mode 100644 index 000000000..3ae498f0f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPresenter.swift @@ -0,0 +1,66 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation +import SwiftUI + +// MARK: - IProductPresenter + +protocol IProductPresenter { + func viewDidLoad() + func purchase(options: PurchaseOptions?) async throws -> StoreTransaction +} + +// MARK: - ProductPresenter + +final class ProductPresenter: IPresenter { + // MARK: Properties + + private let productFetcher: IProductFetcherStrategy + private let purchaseService: IProductPurchaseService + + weak var viewModel: WrapperViewModel? + + // MARK: Initialization + + init( + productFetcher: IProductFetcherStrategy, + purchaseService: IProductPurchaseService + ) { + self.productFetcher = productFetcher + self.purchaseService = purchaseService + } +} + +// MARK: IProductPresenter + +extension ProductPresenter: IProductPresenter { + func viewDidLoad() { + update(state: .loading) + + Task { @MainActor in + do { + let product = try await productFetcher.product() + self.update(state: .product(product)) + } catch { + if let error = error as? IAPError { + self.update(state: .error(error)) + } else { + self.update(state: .error(IAPError.with(error: error))) + } + } + } + } + + @MainActor + func purchase(options: PurchaseOptions?) async throws -> StoreTransaction { + guard case let .product(product) = viewModel?.model.state else { + throw IAPError.unknown + } + + return try await purchaseService.purchase(product: product, options: options) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPurchaseService.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPurchaseService.swift new file mode 100644 index 000000000..e50a65700 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductPurchaseService.swift @@ -0,0 +1,55 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +// MARK: - IProductPurchaseService + +protocol IProductPurchaseService { + func purchase(product: StoreProduct, options: PurchaseOptions?) async throws -> StoreTransaction +} + +// MARK: - ProductPurchaseService + +actor ProductPurchaseService: IProductPurchaseService { + // MARK: Types + + enum Error: Swift.Error { + case alreadyExecuted + } + + // MARK: Properties + + private var iap: IFlare + + private var isExecuted = false + + // MARK: Initialization + + init(iap: IFlare) { + self.iap = iap + } + + // MARK: IPurchaseService + + func purchase(product: StoreProduct, options: PurchaseOptions?) async throws -> StoreTransaction { + guard !isExecuted else { throw Error.alreadyExecuted } + + isExecuted = true + defer { isExecuted = false } + + return try await _purchase(product: product, options: options) + } + + // MARK: Private + + private func _purchase(product: StoreProduct, options: PurchaseOptions?) async throws -> StoreTransaction { + if #available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *), let options = options?.options { + return try await iap.purchase(product: product, options: options) + } else { + return try await iap.purchase(product: product) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductView.swift new file mode 100644 index 000000000..8e5927d63 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductView.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13.0, tvOS 13.0, macOS 10.15, *) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +public struct ProductView: View { + // MARK: Properties + + private let presentationAssembly = PresentationAssembly() + + private let id: String + + // MARK: Initialization + + public init(id: String) { + self.id = id + } + + // MARK: View + + public var body: some View { + presentationAssembly.productViewAssembly.assemble(id: id) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewAssembly.swift new file mode 100644 index 000000000..367be5dea --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewAssembly.swift @@ -0,0 +1,55 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +// MARK: - IProductViewAssembly + +protocol IProductViewAssembly { + func assemble(id: String) -> ViewWrapper + func assemble(storeProduct: StoreProduct) -> ViewWrapper +} + +// MARK: - ProductViewAssembly + +final class ProductViewAssembly: IProductViewAssembly { + // MARK: Properties + + private let iap: IFlare + + // MARK: Initialization + + init(iap: IFlare) { + self.iap = iap + } + + // MARK: IProductViewAssembly + + func assemble(id: String) -> ViewWrapper { + assemble(with: .productID(id)) + } + + func assemble(storeProduct: StoreProduct) -> ViewWrapper { + assemble(with: .product(storeProduct)) + } + + // MARK: Private + + private func assemble(with type: ProductViewType) -> ViewWrapper { + let presenter = ProductPresenter( + productFetcher: ProductStrategy(type: type, iap: iap), + purchaseService: ProductPurchaseService(iap: iap) + ) + let viewModel = WrapperViewModel( + model: ProductViewModel(state: .loading, presenter: presenter) + ) + presenter.viewModel = viewModel + + return ViewWrapper( + viewModel: viewModel + ) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModel.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModel.swift new file mode 100644 index 000000000..5b8a9c9fd --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModel.swift @@ -0,0 +1,26 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - ProductViewModel + +struct ProductViewModel: IModel { + enum State: Equatable { + case loading + case product(StoreProduct) + case error(IAPError) + } + + let state: State + let presenter: IProductPresenter +} + +extension ProductViewModel { + func setState(_ state: State) -> ProductViewModel { + ProductViewModel(state: state, presenter: presenter) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModelFactory.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModelFactory.swift new file mode 100644 index 000000000..f94e4b811 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewModelFactory.swift @@ -0,0 +1,60 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - IProductViewModelFactory + +protocol IProductViewModelFactory { + func make(_ product: StoreProduct, style: ProductStyle) -> ProductInfoView.ViewModel +} + +// MARK: - ProductViewModelFactory + +final class ProductViewModelFactory: IProductViewModelFactory { + // MARK: Properties + + private let subscriptionPriceViewModelFactory: ISubscriptionPriceViewModelFactory + + // MARK: Initialization + + init( + subscriptionPriceViewModelFactory: ISubscriptionPriceViewModelFactory = SubscriptionPriceViewModelFactory() + ) { + self.subscriptionPriceViewModelFactory = subscriptionPriceViewModelFactory + } + + // MARK: IProductViewModelFactory + + func make(_ product: StoreProduct, style: ProductStyle) -> ProductInfoView.ViewModel { + ProductInfoView.ViewModel( + id: product.productIdentifier, + title: product.localizedTitle, + description: product.localizedDescription, + price: makePrice(from: product, style: style), + priceDescription: makePriceDescription(from: product) + ) + } + + // MARK: Private + + private func makePrice(from product: StoreProduct, style: ProductStyle) -> String { + switch style { + case .compact: + return subscriptionPriceViewModelFactory.make(product, format: .short) + case .large: + return subscriptionPriceViewModelFactory.make(product, format: .full) + } + } + + private func makePriceDescription(from product: StoreProduct) -> String? { + let localizedPeriod = subscriptionPriceViewModelFactory.period(from: product) + + guard let string = localizedPeriod?.words.last else { return nil } + + return L10n.Product.priceDescription(string).capitalized + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewType.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewType.swift new file mode 100644 index 000000000..6ab6aab7f --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductViewType.swift @@ -0,0 +1,11 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +enum ProductViewType { + case product(StoreProduct) + case productID(String) +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductWrapperView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductWrapperView.swift new file mode 100644 index 000000000..b1ffff511 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/ProductWrapperView.swift @@ -0,0 +1,77 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +// MARK: - ProductWrapperView + +struct ProductWrapperView: View, IViewWrapper { + // MARK: Properties + + @Environment(\.productViewStyle) var productViewStyle + @Environment(\.purchaseCompletion) var purchaseCompletion + @Environment(\.purchaseOptions) var purchaseOptions + + @State private var error: Error? + @State private var isExecuted = false + + private let viewModel: ProductViewModel + + // MARK: Initialization + + init(viewModel: ProductViewModel) { + self.viewModel = viewModel + } + + // MARK: View + + var body: some View { + contentView + .onLoad { viewModel.presenter.viewDidLoad() } + .errorAlert($error) + } + + // MARK: Private + + @ViewBuilder + private var contentView: some View { + switch viewModel.state { + case .loading: + productViewStyle.makeBody(configuration: .init(state: .loading)) + case let .product(storeProduct): + productViewStyle.makeBody( + configuration: .init( + state: .product(item: storeProduct), + purchase: purchase + ) + ) + case let .error(error): + productViewStyle.makeBody(configuration: .init(state: .error(error: error))) + } + } + + private func purchase() { + guard case let .product(storeProduct) = viewModel.state, !isExecuted else { return } + + isExecuted = true + + Task { @MainActor in + defer { isExecuted = false } + + do { + let options = purchaseOptions?(storeProduct) + let transaction = try await viewModel.presenter.purchase(options: options) + + purchaseCompletion?(storeProduct, .success(transaction)) + } catch { + if error.iap != .paymentCancelled { + self.error = error.iap + purchaseCompletion?(storeProduct, .failure(error)) + } + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/Strategies/ProductStrategy.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/Strategies/ProductStrategy.swift new file mode 100644 index 000000000..841e1f119 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/Strategies/ProductStrategy.swift @@ -0,0 +1,43 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare + +// MARK: - IProductFetcherStrategy + +protocol IProductFetcherStrategy { + func product() async throws -> StoreProduct +} + +// MARK: - ProductStrategy + +final class ProductStrategy: IProductFetcherStrategy { + // MARK: Properties + + private let iap: IFlare + private let type: ProductViewType + + // MARK: Initialization + + init(type: ProductViewType, iap: IFlare) { + self.type = type + self.iap = iap + } + + // MARK: IProductStrategy + + func product() async throws -> StoreProduct { + switch type { + case let .productID(id): + let product = try await iap.fetch(productIDs: [id]).first + + guard let product else { throw IAPError.storeProductNotAvailable } + + return product + case let .product(product): + return product + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/SubscriptionDateComponentsFactory.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/SubscriptionDateComponentsFactory.swift new file mode 100644 index 000000000..0487c6abd --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/SubscriptionDateComponentsFactory.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - ISubscriptionDateComponentsFactory + +protocol ISubscriptionDateComponentsFactory { + func dateComponents(for subscription: SubscriptionPeriod) -> DateComponents +} + +// MARK: - SubscriptionDateComponentsFactory + +final class SubscriptionDateComponentsFactory: ISubscriptionDateComponentsFactory { + func dateComponents(for subscription: SubscriptionPeriod) -> DateComponents { + var dateComponents = DateComponents() + dateComponents.calendar = Calendar.current + + let numberOfUnits = subscription.value + + switch subscription.unit { + case .day: + dateComponents.setValue(numberOfUnits, for: .day) + case .week: + dateComponents.setValue(numberOfUnits, for: .weekOfMonth) + case .month: + dateComponents.setValue(numberOfUnits, for: .month) + case .year: + dateComponents.setValue(numberOfUnits, for: .year) + } + + return dateComponents + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductView/Views/ProductInfoView/ProductInfoView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductView/Views/ProductInfoView/ProductInfoView.swift new file mode 100644 index 000000000..3fdd89851 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductView/Views/ProductInfoView/ProductInfoView.swift @@ -0,0 +1,207 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProductInfoView + +struct ProductInfoView: View { + // MARK: Types + + enum Style { + @available(iOS 13.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + case large + case compact + } + + // MARK: Properties + + private let viewModel: ViewModel + private let icon: ProductStyleConfiguration.Icon? + private let style: Style + private let action: () -> Void + + // MARK: Initialization + + init( + viewModel: ViewModel, + icon: ProductStyleConfiguration.Icon?, + style: Style, + action: @escaping () -> Void + ) { + self.viewModel = viewModel + self.icon = icon + self.style = style + self.action = action + } + + // MARK: View + + var body: some View { + #if os(tvOS) + Button(action: { + action() + }, label: { + contentView + }) + #else + contentView + #endif + } + + // MARK: Private + + private var contentView: some View { + stackView(spacing: metrics(compact: nil, large: .largeSpacing)) { + iconView + + textView + Spacer(minLength: .zero) + priceView + } + .padding(.padding) + .frame(height: .height) + } + + private var iconView: some View { + icon.map { $0 } + .frame( + idealHeight: metrics(compact: nil, large: .largeImageHeight), + maxHeight: metrics(compact: nil, large: .largeImageHeight) + ) + } + + private var textView: some View { + let alignment: HorizontalAlignment = { + switch style { + case .compact: + return .leading + case .large: + return .center + } + }() + + return VStack(alignment: alignment) { + Text(viewModel.title) + .lineLimit(.lineLimit) + .font(.body) + Text(viewModel.description) + .lineLimit(.lineLimit) + .font(.caption) + .foregroundColor(Palette.systemGray) + #if os(tvOS) + Spacer() + #endif + } + } + + private var priceView: some View { + #if os(tvOS) + Text(viewModel.price) + #else + VStack(alignment: .center) { + Button( + action: { + action() + }, + label: { + Text(viewModel.price) + .font(.subheadline) + .fontWeight(.bold) + } + ) + .buttonStyle(BorderedButtonStyle()) + + if style == .compact { + priceDescriptionView + } + } + #endif + } + + private var priceDescriptionView: some View { + viewModel.priceDescription.map { + Text($0) + .font(.system(size: .priceDescriptionFontSize)) + .foregroundColor(Palette.systemGray) + } + } + + private func stackView(spacing: CGFloat? = nil, @ViewBuilder content: () -> some View) -> some View { + Group { + switch style { + case .compact: + HStack(alignment: .center, spacing: spacing) { + content() + } + case .large: + VStack(alignment: .center, spacing: spacing) { + content() + } + } + } + } + + private func metrics(compact: T?, large: T?) -> T? { + #if os(iOS) + switch style { + case .compact: + return compact + case .large: + return large ?? compact + } + #else + return compact + #endif + } +} + +// MARK: ProductInfoView.ViewModel + +extension ProductInfoView { + struct ViewModel: Identifiable { + let id: String + let title: String + let description: String + let price: String + let priceDescription: String? + } +} + +// MARK: - Constants + +private extension CGFloat { + static let height = value(default: 56, tvOS: 200.0) + static let padding = value(default: .zero, tvOS: 24.0) + static let priceDescriptionFontSize = value(default: 10.0) + static let largeImageHeight = 140.0 + static let largeSpacing = 14.0 +} + +private extension Int { + static let lineLimit = 2 +} + +// MARK: Preview + +#if swift(>=5.9) + #Preview { + ProductInfoView( + viewModel: .init( + id: UUID().uuidString, + title: "My App Lifetime", + description: "Lifetime access to additional content", + price: "$19.99", + priceDescription: "Every Month" + ), + icon: nil, + style: .compact, + action: {} + ) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsPresenter.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsPresenter.swift new file mode 100644 index 000000000..c8c1c5390 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsPresenter.swift @@ -0,0 +1,46 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - IProductsPresenter + +protocol IProductsPresenter { + func viewDidLoad() +} + +// MARK: - ProductsPresenter + +final class ProductsPresenter: IPresenter { + // MARK: Properties + + private let ids: any Collection + private let iap: IFlare + + weak var viewModel: WrapperViewModel? + + // MARK: Initialization + + init(ids: some Collection, iap: IFlare) { + self.ids = ids + self.iap = iap + } +} + +// MARK: IProductsPresenter + +extension ProductsPresenter: IProductsPresenter { + func viewDidLoad() { + Task { @MainActor in + do { + let products = try await iap.fetch(productIDs: ids) + self.update(state: .products(products)) + } catch { + self.update(state: .error(error.iap)) + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsView.swift new file mode 100644 index 000000000..176efab52 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsView.swift @@ -0,0 +1,27 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +public struct ProductsView: View { + // MARK: Properties + + private let presentationAssembly = PresentationAssembly() + + private let ids: any Collection + + // MARK: Initialization + + public init(ids: some Collection) { + self.ids = ids + } + + // MARK: View + + public var body: some View { + presentationAssembly.productsViewAssembly.assemble(ids: ids) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewAssembly.swift new file mode 100644 index 000000000..3d7b22d5e --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewAssembly.swift @@ -0,0 +1,52 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +// MARK: - IProductsViewAssembly + +protocol IProductsViewAssembly { + func assemble(ids: some Collection) -> AnyView +} + +// MARK: - ProductsViewAssembly + +final class ProductsViewAssembly: IProductsViewAssembly { + // MARK: Properties + + private let storeButtonsAssembly: IStoreButtonsAssembly + private let productAssembly: IProductViewAssembly + private let iap: IFlare + + // MARK: Initialization + + init(productAssembly: IProductViewAssembly, storeButtonsAssembly: IStoreButtonsAssembly, iap: IFlare) { + self.productAssembly = productAssembly + self.storeButtonsAssembly = storeButtonsAssembly + self.iap = iap + } + + // MARK: IProductsViewAssembly + + func assemble(ids: some Collection) -> AnyView { + let presenter = ProductsPresenter( + ids: ids, + iap: iap + ) + let viewModel = WrapperViewModel( + model: ProductsViewModel( + state: .loading(ids.count), + presenter: presenter + ) + ) + presenter.viewModel = viewModel + + return ViewWrapper(viewModel: viewModel) + .environment(\.productViewAssembly, productAssembly) + .environment(\.storeButtonsAssembly, storeButtonsAssembly) + .eraseToAnyView() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewModel.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewModel.swift new file mode 100644 index 000000000..0c787c79a --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsViewModel.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - ProductsViewModel + +struct ProductsViewModel: IModel { + enum State: Equatable { + case loading(Int) + case products([StoreProduct]) + case error(IAPError) + } + + let state: State + let presenter: IProductsPresenter +} + +extension ProductsViewModel { + func setState(_ state: State) -> ProductsViewModel { + ProductsViewModel( + state: state, + presenter: presenter + ) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsWrapperView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsWrapperView.swift new file mode 100644 index 000000000..fe31bd0ea --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/ProductsWrapperView.swift @@ -0,0 +1,72 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - ProductsWrapperView + +struct ProductsWrapperView: View, IViewWrapper { + // MARK: Properties + + @Environment(\.productViewStyle) var productViewStyle + @Environment(\.storeButton) var storeButton + @Environment(\.productViewAssembly) var productViewAssembly + @Environment(\.storeButtonsAssembly) var storeButtonsAssembly + + private let viewModel: ProductsViewModel + + // MARK: Initialization + + init(viewModel: ProductsViewModel) { + self.viewModel = viewModel + } + + // MARK: View + + var body: some View { + contentView + .onAppear { viewModel.presenter.viewDidLoad() } + .padding() + } + + // MARK: Private + + @ViewBuilder + private var contentView: some View { + switch viewModel.state { + case let .loading(numberOfItems): + contentView { + ForEach(0 ..< numberOfItems, id: \.self) { _ in + productViewStyle.makeBody(configuration: .init(state: .loading)) + } + } + case let .products(products): + contentView { + ForEach(Array(products), id: \.self) { product in + productViewAssembly.map { $0.assemble(storeProduct: product) } + } + } + case .error: + StoreUnavaliableView(productType: .product) + } + } + + private var storeButtonView: some View { + ForEach(storeButton, id: \.self) { type in + storeButtonsAssembly.map { $0.assemble(storeButtonType: type) } + } + } + + private func contentView(@ViewBuilder content: () -> Content) -> some View { + VStack(alignment: .center) { + ScrollView { + content() + .padding(.horizontal) + }.animation(nil, value: UUID()) + Spacer() + storeButtonView + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/ProductsView/Views/StoreUnavaliableView.swift b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/Views/StoreUnavaliableView.swift new file mode 100644 index 000000000..6ef24c0b1 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/ProductsView/Views/StoreUnavaliableView.swift @@ -0,0 +1,56 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - StoreUnavaliableView + +struct StoreUnavaliableView: View { + // MARK: Types + + enum ProductType { + case product + case subscription + + var message: String { + switch self { + case .product: + return L10n.StoreUnavailable.Product.message + case .subscription: + return L10n.StoreUnavailable.Subscription.message + } + } + } + + // MARK: Properties + + private let productType: ProductType + + // MARK: Initialization + + init(productType: ProductType) { + self.productType = productType + } + + // MARK: View + + var body: some View { + VStack { + Text(L10n.StoreUnavailable.title) + .font(.title) + Text(productType.message) + .font(.body) + .multilineTextAlignment(.center) + .foregroundColor(Palette.systemGray) + } + .padding() + } +} + +#if swift(>=5.9) + #Preview { + StoreUnavaliableView(productType: .product) + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButton.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButton.swift new file mode 100644 index 000000000..b73f8d72b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButton.swift @@ -0,0 +1,11 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +enum StoreButton { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + case restore +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonAssembly.swift new file mode 100644 index 000000000..caff51ba6 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonAssembly.swift @@ -0,0 +1,53 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - IStoreButtonAssembly + +protocol IStoreButtonAssembly { + func assemble(storeButtonType: StoreButton) -> ViewWrapper +} + +// MARK: - StoreButtonAssembly + +final class StoreButtonAssembly: IStoreButtonAssembly { + // MARK: Properties + + private let iap: IFlare + + // MARK: Initialization + + init(iap: IFlare) { + self.iap = iap + } + + // MARK: IStoreButtonAssembly + + func assemble(storeButtonType: StoreButton) -> ViewWrapper { + let presenter = StoreButtonPresenter(iap: iap) + let viewModel = WrapperViewModel( + model: StoreButtonViewModel( + state: map(storeButtonType: storeButtonType), + presenter: presenter + ) + ) + presenter.viewModel = viewModel + + return ViewWrapper( + viewModel: viewModel + ) + } + + // MARK: Private + + private func map(storeButtonType: StoreButton) -> StoreButtonViewModel.State { + switch storeButtonType { + case .restore: + return .restore(viewModel: .init(title: L10n.StoreButton.restorePurchases)) + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonPresenter.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonPresenter.swift new file mode 100644 index 000000000..b70e69778 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonPresenter.swift @@ -0,0 +1,45 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - IStoreButtonPresenter + +protocol IStoreButtonPresenter { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws +} + +// MARK: - StoreButtonPresenter + +final class StoreButtonPresenter: IPresenter { + // MARK: Properties + + private let iap: IFlare + + weak var viewModel: WrapperViewModel? + + // MARK: Initialization + + init(iap: IFlare) { + self.iap = iap + } +} + +// MARK: IStoreButtonPresenter + +extension StoreButtonPresenter: IStoreButtonPresenter { + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws { + do { + try await iap.restore() + } catch { + if let error = error as? IAPError, error != .paymentCancelled { + throw error + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonView.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonView.swift new file mode 100644 index 000000000..6e06319b7 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonView.swift @@ -0,0 +1,50 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - StoreButtonView + +struct StoreButtonView: View, IViewWrapper { + // MARK: Properties + + @Environment(\.storeButtonViewFontWeight) private var storeButtonViewFontWeight + + private let viewModel: StoreButtonViewModel + @State private var error: Error? + + // MARK: Initialization + + init(viewModel: StoreButtonViewModel) { + self.viewModel = viewModel + } + + // MARK: View + + var body: some View { + contentView + .errorAlert($error) + } + + // MARK: Private + + @ViewBuilder + private var contentView: some View { + Button(action: { + Task { + do { + if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { + try await viewModel.presenter.restore() + } + } catch { + self.error = error + } + } + }, label: { + Text(viewModel.state.title) + .fontWeight(storeButtonViewFontWeight) + }) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonViewModel.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonViewModel.swift new file mode 100644 index 000000000..69997b04d --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonView/StoreButtonViewModel.swift @@ -0,0 +1,34 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Foundation + +// MARK: - StoreButtonViewModel + +struct StoreButtonViewModel: IModel { + struct ViewModel: Equatable { + let title: String + } + + enum State: Equatable { + case restore(viewModel: ViewModel) + + var title: String { + switch self { + case let .restore(viewModel): + return viewModel.title + } + } + } + + let state: State + let presenter: IStoreButtonPresenter +} + +extension StoreButtonViewModel { + func setState(_ state: State) -> StoreButtonViewModel { + StoreButtonViewModel(state: state, presenter: presenter) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/StoreButtonsView/StoreButtonsAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonsView/StoreButtonsAssembly.swift new file mode 100644 index 000000000..749d17819 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/StoreButtonsView/StoreButtonsAssembly.swift @@ -0,0 +1,44 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - IStoreButtonsAssembly + +protocol IStoreButtonsAssembly { + func assemble(storeButtonType: StoreButtonType) -> AnyView +} + +// MARK: - StoreButtonsAssembly + +@available(watchOS, unavailable) +final class StoreButtonsAssembly: IStoreButtonsAssembly { + // MARK: Properties + + private let storeButtonAssembly: IStoreButtonAssembly + private let policiesButtonAssembly: IPoliciesButtonAssembly + + // MARK: Initialization + + init(storeButtonAssembly: IStoreButtonAssembly, policiesButtonAssembly: IPoliciesButtonAssembly) { + self.storeButtonAssembly = storeButtonAssembly + self.policiesButtonAssembly = policiesButtonAssembly + } + + // MARK: IStoreButtonsAssembly + + func assemble(storeButtonType: StoreButtonType) -> AnyView { + switch storeButtonType { + case .restore: + return Group { + if #available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *) { + storeButtonAssembly.assemble(storeButtonType: .restore) + } + }.eraseToAnyView() + case .policies: + return policiesButtonAssembly.assemble().eraseToAnyView() + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/AnySubscriptionsWrapperViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/AnySubscriptionsWrapperViewStyle.swift new file mode 100644 index 000000000..c30945dc9 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/AnySubscriptionsWrapperViewStyle.swift @@ -0,0 +1,32 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +struct AnySubscriptionsWrapperViewStyle: ISubscriptionsWrapperViewStyle { + // MARK: Properties + + /// A private property to hold the closure that creates the body of the view + private var _makeBody: (Configuration) -> AnyView + + // MARK: Initialization + + /// Initializes the `AnyProductStyle` with a specific style conforming to `IProductStyle`. + /// + /// - Parameter style: A product style. + init(style: S) { + _makeBody = { configuration in + AnyView(style.makeBody(configuration: configuration)) + } + } + + // MARK: IProductStyle + + /// Implements the makeBody method required by `IProductStyle`. + func makeBody(configuration: Configuration) -> some View { + _makeBody(configuration) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Configuration/SubscriptionsWrapperViewStyleConfiguration.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Configuration/SubscriptionsWrapperViewStyleConfiguration.swift new file mode 100644 index 000000000..4d0510c36 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Configuration/SubscriptionsWrapperViewStyleConfiguration.swift @@ -0,0 +1,26 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +// swiftlint:disable:next type_name +struct SubscriptionsWrapperViewStyleConfiguration { + // MARK: Types + + struct Toolbar: View { + let body: AnyView + + init(_ content: Content) { + self.body = content.eraseToAnyView() + } + } + + // MARK: Properties + + let items: [SubscriptionView.ViewModel] + let selectedID: String? + let action: (SubscriptionView.ViewModel) -> Void +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/ISubscriptionsWrapperViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/ISubscriptionsWrapperViewStyle.swift new file mode 100644 index 000000000..5d45683cd --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/ISubscriptionsWrapperViewStyle.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +protocol ISubscriptionsWrapperViewStyle { + /// A view that represents the body of an in-app subscription store control. + associatedtype Body: View + + /// The properties of an in-app subscription store control. + typealias Configuration = SubscriptionsWrapperViewStyleConfiguration + + /// Creates a view that represents the body of an in-app subscription store control. + /// + /// - Parameter configuration: The properties of an in-app subscription store control. + @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Automatic/AutomaticSubscriptionsWrapperViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Automatic/AutomaticSubscriptionsWrapperViewStyle.swift new file mode 100644 index 000000000..01137928b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Automatic/AutomaticSubscriptionsWrapperViewStyle.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +struct AutomaticSubscriptionsWrapperViewStyle: ISubscriptionsWrapperViewStyle { + func makeBody(configuration: Configuration) -> some View { + #if os(iOS) || os(macOS) + return FullSubscriptionsWrapperViewStyle().makeBody(configuration: configuration) + #else + return CompactSubscriptionWrapperViewStyle().makeBody(configuration: configuration) + #endif + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperView.swift new file mode 100644 index 000000000..a5548ecc1 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperView.swift @@ -0,0 +1,46 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(tvOS 13.0, *) +@available(macOS, unavailable) +@available(iOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct CompactSubscriptionWrapperView: View { + // MARK: Properties + + @Environment(\.subscriptionMarketingContent) private var subscriptionMarketingContent + + private let configuration: SubscriptionsWrapperViewStyleConfiguration + + // MARK: Initialization + + init(configuration: SubscriptionsWrapperViewStyleConfiguration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + VStack { + subscriptionMarketingContent.map { content in + content.frame(maxWidth: .infinity, minHeight: 150.0) + } + + ScrollView(.horizontal) { + HStack { + ForEach(configuration.items) { item in + SubscriptionView(viewModel: item, isSelected: .constant(false)) { + configuration.action(item) + } + } + .frame(maxHeight: .infinity) + } + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperViewStyle.swift new file mode 100644 index 000000000..0c10d60c2 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Compact/CompactSubscriptionWrapperViewStyle.swift @@ -0,0 +1,17 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(tvOS 13.0, *) +@available(macOS, unavailable) +@available(iOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct CompactSubscriptionWrapperViewStyle: ISubscriptionsWrapperViewStyle { + func makeBody(configuration: Configuration) -> some View { + CompactSubscriptionWrapperView(configuration: configuration) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperView.swift new file mode 100644 index 000000000..17ba05c45 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperView.swift @@ -0,0 +1,59 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13.0, macOS 10.15, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +struct FullSubscriptionsWrapperView: View { + // MARK: Properties + + @Environment(\.subscriptionMarketingContent) private var subscriptionMarketingContent + @Environment(\.subscriptionBackground) private var subscriptionBackground + + private let configuration: SubscriptionsWrapperViewStyleConfiguration + + // MARK: Initialization + + init(configuration: SubscriptionsWrapperViewStyleConfiguration) { + self.configuration = configuration + } + + // MARK: View + + var body: some View { + VStack(spacing: .zero) { + productsView(products: configuration.items) + } + .background(subscriptionBackground.edgesIgnoringSafeArea(.all)) + } + + // MARK: Private + + private func productsView(products: [SubscriptionView.ViewModel]) -> some View { + VStack(alignment: .center, spacing: .zero) { + GeometryReader { geo in + ScrollView(.vertical) { + SubscriptionHeaderView(topInset: geo.safeAreaInsets.top) + + VStack { + ForEach(products) { viewModel in + SubscriptionView( + viewModel: viewModel, + isSelected: .constant(viewModel.id == configuration.selectedID) + ) { + self.configuration.action(viewModel) + } + .padding(.horizontal) + } + } + .padding(.bottom) + } + .edgesIgnoringSafeArea(subscriptionMarketingContent != nil ? .top : []) + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperViewStyle.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperViewStyle.swift new file mode 100644 index 000000000..c3e094750 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Styles/SubscriptionsWrapperViewStyle/Styles/Full/FullSubscriptionsWrapperViewStyle.swift @@ -0,0 +1,15 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13.0, macOS 10.15, *) +@available(watchOS, unavailable) +@available(tvOS, unavailable) +struct FullSubscriptionsWrapperViewStyle: ISubscriptionsWrapperViewStyle { + func makeBody(configuration: Configuration) -> some View { + FullSubscriptionsWrapperView(configuration: configuration) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsAssembly.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsAssembly.swift new file mode 100644 index 000000000..c90e05e7b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsAssembly.swift @@ -0,0 +1,56 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import SwiftUI + +// MARK: - ISubscriptionsAssembly + +protocol ISubscriptionsAssembly { + func assemble(ids: some Collection) -> AnyView +} + +// MARK: - SubscriptionsAssembly + +@available(watchOS, unavailable) +final class SubscriptionsAssembly: ISubscriptionsAssembly { + // MARK: Properties + + private let iap: IFlare + private let storeButtonsAssembly: IStoreButtonsAssembly + private let subscriptionStatusVerifierProvider: ISubscriptionStatusVerifierProvider + + // MARK: Initialization + + init( + iap: IFlare, + storeButtonsAssembly: IStoreButtonsAssembly, + subscriptionStatusVerifierProvider: ISubscriptionStatusVerifierProvider + ) { + self.iap = iap + self.storeButtonsAssembly = storeButtonsAssembly + self.subscriptionStatusVerifierProvider = subscriptionStatusVerifierProvider + } + + // MARK: ISubscriptionAssembly + + func assemble(ids: some Collection) -> AnyView { + let viewModelFactory = SubscriptionsViewModelViewFactory( + subscriptionStatusVerifier: subscriptionStatusVerifierProvider.subscriptionStatusVerifier + ) + let presenter = SubscriptionsPresenter(iap: iap, ids: ids, viewModelFactory: viewModelFactory) + let viewModel = WrapperViewModel( + model: SubscriptionsViewModel( + state: .loading, + selectedProductID: ids.first, + presenter: presenter + ) + ) + presenter.viewModel = viewModel + return ViewWrapper(viewModel: viewModel) + .environment(\.storeButtonsAssembly, storeButtonsAssembly) + .eraseToAnyView() + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsPresenter.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsPresenter.swift new file mode 100644 index 000000000..c6550de38 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsPresenter.swift @@ -0,0 +1,99 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation +import StoreKit + +// MARK: - ISubscriptionsPresenter + +protocol ISubscriptionsPresenter { + func viewDidLoad() + func selectProduct(with id: String) + func product(withID id: String) -> StoreProduct? + func subscribe(optionsHandler: PurchaseOptionHandler?) async throws -> StoreTransaction +} + +// MARK: - SubscriptionsPresenter + +@available(watchOS, unavailable) +final class SubscriptionsPresenter: IPresenter { + // MARK: Properties + + private let iap: IFlare + private let ids: any Collection + private let viewModelFactory: ISubscriptionsViewModelViewFactory + + private var products: [StoreProduct] = [] + + weak var viewModel: WrapperViewModel? + + // MARK: Initialization + + init(iap: IFlare, ids: some Collection, viewModelFactory: ISubscriptionsViewModelViewFactory) { + self.iap = iap + self.ids = ids + self.viewModelFactory = viewModelFactory + } + + // MARK: Private + + private func loadProducts() { + Task { @MainActor in + do { + self.products = try await iap.fetch(productIDs: ids) + .filter { $0.productType == .autoRenewableSubscription } + + guard !products.isEmpty else { + update(state: .error(.storeProductNotAvailable)) + return + } + + let viewModel = try await viewModelFactory.make(products) + + update(state: .products(viewModel)) + } catch { + update(state: .error(error.iap)) + } + } + } +} + +// MARK: ISubscriptionsPresenter + +@available(watchOS, unavailable) +extension SubscriptionsPresenter: ISubscriptionsPresenter { + func viewDidLoad() { + loadProducts() + } + + func product(withID id: String) -> StoreProduct? { + products.by(id: id) + } + + func selectProduct(with id: String) { + guard let model = self.viewModel?.model else { return } + self.viewModel?.model = model.setSelectedProductID(id) + } + + func subscribe(optionsHandler: PurchaseOptionHandler?) async throws -> StoreTransaction { + guard let id = self.viewModel?.model.selectedProductID, let product = products.by(id: id) else { + throw IAPError.unknown + } + + let options = optionsHandler?(product) + let transaction: StoreTransaction + + if #available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 8.0, *), let options = options?.options { + transaction = try await iap.purchase(product: product, options: options) + } else { + transaction = try await iap.purchase(product: product) + } + + loadProducts() + + return transaction + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsView.swift new file mode 100644 index 000000000..839d43955 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsView.swift @@ -0,0 +1,29 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(iOS 13.0, tvOS 13.0, macOS 11.0, *) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +public struct SubscriptionsView: View { + // MARK: Properties + + private let presentationAssembly = PresentationAssembly() + + private let ids: any Collection + + // MARK: Initialization + + public init(ids: some Collection) { + self.ids = ids + } + + // MARK: View + + public var body: some View { + presentationAssembly.subscritpionsViewAssembly.assemble(ids: ids) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModel.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModel.swift new file mode 100644 index 000000000..717f065a5 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModel.swift @@ -0,0 +1,48 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - SubscriptionsViewModel + +@available(watchOS, unavailable) +struct SubscriptionsViewModel: IModel { + enum State: Equatable { + case loading + case products([SubscriptionView.ViewModel]) + case error(IAPError) + } + + var numberOfProducts: Int { + if case let .products(array) = state { + return array.count + } + return .zero + } + + let state: State + let selectedProductID: String? + let presenter: ISubscriptionsPresenter +} + +@available(watchOS, unavailable) +extension SubscriptionsViewModel { + func setState(_ state: State) -> SubscriptionsViewModel { + SubscriptionsViewModel( + state: state, + selectedProductID: selectedProductID, + presenter: presenter + ) + } + + func setSelectedProductID(_ selectedProductID: String?) -> SubscriptionsViewModel { + SubscriptionsViewModel( + state: state, + selectedProductID: selectedProductID, + presenter: presenter + ) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModelViewFactory.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModelViewFactory.swift new file mode 100644 index 000000000..c7429772b --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsViewModelViewFactory.swift @@ -0,0 +1,69 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import Foundation + +// MARK: - ISubscriptionsViewModelViewFactory + +@available(watchOS, unavailable) +protocol ISubscriptionsViewModelViewFactory { + func make(_ products: [StoreProduct]) async throws -> [SubscriptionView.ViewModel] +} + +// MARK: - SubscriptionsViewModelViewFactory + +@available(watchOS, unavailable) +final class SubscriptionsViewModelViewFactory: ISubscriptionsViewModelViewFactory { + // MARK: Properties + + private let subscriptionPriceViewModelFactory: ISubscriptionPriceViewModelFactory + private let subscriptionStatusVerifier: ISubscriptionStatusVerifier? + + // MARK: Initialization + + init( + subscriptionPriceViewModelFactory: ISubscriptionPriceViewModelFactory = SubscriptionPriceViewModelFactory(), + subscriptionStatusVerifier: ISubscriptionStatusVerifier? = nil + ) { + self.subscriptionPriceViewModelFactory = subscriptionPriceViewModelFactory + self.subscriptionStatusVerifier = subscriptionStatusVerifier + } + + // MARK: ISubscriptionsViewModelViewFactory + + func make(_ products: [StoreProduct]) async throws -> [SubscriptionView.ViewModel] { + var viewModels: [SubscriptionView.ViewModel] = [] + + for product in products { + let viewModel = try SubscriptionView.ViewModel( + id: product.productIdentifier, + title: product.localizedTitle, + price: makePrice(string: subscriptionPriceViewModelFactory.make(product, format: .full)), + description: product.localizedDescription, + isActive: await validationSubscriptionStatus(product) + ) + + viewModels.append(viewModel) + } + + return viewModels + } + + // MARK: Private + + private func validationSubscriptionStatus(_ product: StoreProduct) async throws -> Bool { + guard let subscriptionStatusVerifier = subscriptionStatusVerifier else { return false } + return try await subscriptionStatusVerifier.validate(product) + } + + private func makePrice(string: String) -> String { + #if os(tvOS) + return L10n.Subscriptions.Renewable.subscriptionDescriptionSeparated(string) + #else + return string + #endif + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsWrapperView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsWrapperView.swift new file mode 100644 index 000000000..9e2b014cd --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/SubscriptionsWrapperView.swift @@ -0,0 +1,136 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +@available(watchOS, unavailable) +struct SubscriptionsWrapperView: View, IViewWrapper { + // MARK: Propertirs + + @Environment(\.subscriptionsWrapperViewStyle) private var subscriptionsWrapperViewStyle + + @Environment(\.purchaseCompletion) private var purchaseCompletion + @Environment(\.purchaseOptions) private var purchaseOptions + @Environment(\.subscriptionBackground) private var subscriptionBackground + @Environment(\.subscriptionControlStyle) private var subscriptionControlStyle + @Environment(\.storeButtonsAssembly) private var storeButtonsAssembly + @Environment(\.storeButton) private var storeButton + + @State private var selectedProduct: SubscriptionView.ViewModel? + @State private var error: Error? + + @State private var isLoading = false + + private var isButtonsStyle: Bool { + subscriptionControlStyle.style is ButtonSubscriptionStoreControlStyle + } + + private let viewModel: SubscriptionsViewModel + + // MARK: Initialization + + init(viewModel: SubscriptionsViewModel) { + self.viewModel = viewModel + } + + // MARK: View + + var body: some View { + contentView + .onAppear { viewModel.presenter.viewDidLoad() } + .errorAlert($error) + } + + // MARK: Private + + @ViewBuilder + private var contentView: some View { + switch viewModel.state { + case .loading: + loadingView + case let .products(products): + VStack(spacing: .zero) { + productsView(products: products) + .onAppear { selectedProduct = products.first(where: { $0.id == viewModel.selectedProductID }) } + + #if os(iOS) + if !isButtonsStyle { + toolbarView + } + #else + if storeButton.contains(.policies) { + storeButtonsAssembly?.assemble(storeButtonType: .policies) + } + #endif + } + .background(subscriptionBackground.edgesIgnoringSafeArea(.all)) + .activityIndicator(isLoading: isLoading) + case .error: + StoreUnavaliableView(productType: .subscription) + } + } + + private func productsView(products: [SubscriptionView.ViewModel]) -> some View { + subscriptionsWrapperViewStyle.makeBody( + configuration: .init( + items: products, + selectedID: selectedProduct?.id, + action: { product in + if isButtonsStyle { + self.purchase(productID: product.id) + } else { + self.selectedProduct = product + self.viewModel.presenter.selectProduct(with: product.id) + } + } + ) + ) + } + + private var loadingView: some View { + LoadingView(message: L10n.Subscription.Loading.message) + } + + #if os(iOS) + private var toolbarView: some View { + selectedProduct.map { product in + SubscriptionToolbarView( + viewModel: .init( + id: product.id, + title: product.title, + price: product.price, + description: product.description, + isActive: product.isActive + ), + action: { self.purchase(productID: product.id) } + ) + } + } + #endif + + private func purchase(productID: String) { + guard let product = viewModel.presenter.product(withID: productID) else { return } + + withAnimation { + isLoading = true + } + + Task { + do { + let transaction = try await self.viewModel.presenter.subscribe(optionsHandler: purchaseOptions) + purchaseCompletion?(product, .success(transaction)) + } catch { + if error.iap != .paymentCancelled { + self.error = error.iap + purchaseCompletion?(product, .failure(error)) + } + } + + withAnimation { + isLoading = false + } + } + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/LoadingView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/LoadingView.swift new file mode 100644 index 000000000..3093b5c9c --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/LoadingView.swift @@ -0,0 +1,79 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +struct LoadingView: View { + // MARK: Types + + enum LoadingViewType { + case `default` + case backgrouned + } + + // MARK: Properties + + private let type: LoadingViewType + private let message: String + + // MARK: Initialization + + init(type: LoadingViewType = .default, message: String) { + self.type = type + self.message = message + } + + // MARK: View + + var body: some View { + switch type { + case .default: + loadingView + case .backgrouned: + loadingView + .padding() + .background( + RoundedRectangle(cornerRadius: 8.0) + .fill(Palette.systemBackground) + ) + } + } + + // MARK: Private + + private var loadingView: some View { + VStack(alignment: .center, spacing: 52) { + if #available(iOS 15.0, tvOS 15.0, macOS 12.0, watchOS 9.0, *) { + #if os(tvOS) + progressView + #else + progressView + .controlSize(.large) + #endif + } else if #available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *) { + progressView + .scaleEffect(1.74) + } else { + #if os(macOS) + ActivityIndicatorView(isAnimating: .constant(true)) + #elseif os(watchOS) + Text("Loading...") + #else + ActivityIndicatorView(isAnimating: .constant(true), style: .large) + #endif + } + + Text(message) + .font(.subheadline) + .foregroundColor(Palette.systemGray) + } + } + + @available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *) + private var progressView: some View { + ProgressView() + .progressViewStyle(.circular) + } +} diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionHeaderView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionHeaderView.swift new file mode 100644 index 000000000..e16bcf39a --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionHeaderView.swift @@ -0,0 +1,69 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionHeaderView + +@available(iOS 13.0, macOS 10.15, *) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct SubscriptionHeaderView: View { + // MARK: Properties + + @Environment(\.storeButtonsAssembly) private var storeButtonsAssembly + @Environment(\.storeButton) private var storeButton + @Environment(\.subscriptionMarketingContent) private var subscriptionMarketingContent + @Environment(\.subscriptionHeaderContentBackground) private var subscriptionHeaderContentBackground + @Environment(\.subscriptionViewTint) private var subscriptionViewTint + + private let topInset: CGFloat + + // MARK: Initialization + + init(topInset: CGFloat = .zero) { + self.topInset = topInset + } + + // MARK: View + + var body: some View { + VStack { + subscriptionMarketingContent.map { content in + content.frame(maxWidth: .infinity, minHeight: 250.0) + .padding(.top, topInset) + } + + policiesButton + .tintColor(subscriptionViewTint) + .padding(.bottom) + } + .background(headerBackground) + } + + // MARK: Private + + @ViewBuilder + private var headerBackground: some View { + if subscriptionMarketingContent != nil { + subscriptionHeaderContentBackground.edgesIgnoringSafeArea(.all) + } + } + + private var policiesButton: some View { + Group { + if storeButton.contains(.policies) { + storeButtonsAssembly?.assemble(storeButtonType: .policies) + } + } + } +} + +#if swift(>=5.9) && os(iOS) + #Preview { + SubscriptionHeaderView() + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionToolbarView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionToolbarView.swift new file mode 100644 index 000000000..c3754c919 --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionToolbarView.swift @@ -0,0 +1,137 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionToolbarView + +@available(iOS 13.0, *) +@available(macOS, unavailable) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +struct SubscriptionToolbarView: View { + // MARK: Properties + + @Environment(\.storeButtonsAssembly) private var storeButtonsAssembly + @Environment(\.storeButton) private var storeButton + @Environment(\.subscriptionViewTint) private var subscriptionViewTint + @Environment(\.subscriptionBackground) private var subscriptionBackground + + private let viewModel: ViewModel + private let action: () -> Void + + // MARK: Initialization + + init(viewModel: ViewModel, action: @escaping () -> Void) { + self.viewModel = viewModel + self.action = action + } + + // MARK: View + + var body: some View { + bottomToolbar { purchaseContainer } + .background( + Color.clear + #if os(iOS) || os(tvOS) + .blurEffect() + .edgesIgnoringSafeArea(.all) + #endif + ) + } + + // MARK: Private + + private var purchaseContainer: some View { + VStack(spacing: 24.0) { + subscriptionsDetailsView { + SubscriptionView( + viewModel: .init( + id: viewModel.id, + title: viewModel.title, + price: viewModel.price, + description: viewModel.description, + isActive: viewModel.isActive + ), + isSelected: .constant(false), + action: action + ) + .subscriptionControlStyle(.button) + .subscriptionButtonLabel(.multiline) + .padding(.horizontal) + } + + storeButtonView + } + } + + private func bottomToolbar(@ViewBuilder content: () -> some View) -> some View { + content() + .padding(.top) + } + + private var storeButtonView: some View { + Group { + if #available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) { + if storeButton.contains(.restore) { + storeButtonsAssembly?.assemble(storeButtonType: .restore) + .storeButtonViewFontWeight(.bold) + .foregroundColor(subscriptionViewTint) + } + } + } + } + + private var subscriptionsDetailsView: some View { + Text(L10n.Subscriptions.Renewable.subscriptionDescription(viewModel.price)) + .font(.footnote) + .multilineTextAlignment(.center) + .contrast(subscriptionBackground) + } + + private func subscriptionsDetailsView(@ViewBuilder content: () -> some View) -> some View { + VStack(spacing: 6.0) { + content() + subscriptionsDetailsView + .contrast(subscriptionBackground) + .font(.subheadline) + } + } +} + +// MARK: SubscriptionToolbarView.ViewModel + +@available(iOS 13.0, *) +@available(macOS, unavailable) +@available(tvOS, unavailable) +@available(watchOS, unavailable) +@available(visionOS, unavailable) +extension SubscriptionToolbarView { + struct ViewModel { + let id: String + let title: String + let price: String + let description: String + let isActive: Bool + } +} + +#if swift(>=5.9) && os(iOS) + #Preview { + VStack { + SubscriptionToolbarView( + viewModel: .init( + id: "", + title: "Subscription", + price: "$0.99/month", + description: "Description", + isActive: true + ), + action: {} + ) + } + } +#endif diff --git a/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionView.swift b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionView.swift new file mode 100644 index 000000000..f3fa47abe --- /dev/null +++ b/Sources/FlareUI/Classes/Presentation/Views/SubscriptionsView/Views/SubscriptionView.swift @@ -0,0 +1,102 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SwiftUI + +// MARK: - SubscriptionView + +@available(watchOS, unavailable) +struct SubscriptionView: View { + // MARK: Properties + + @Environment(\.subscriptionControlStyle) private var subscriptionControlStyle + + @Binding private var isSelected: Bool + + private let viewModel: ViewModel + private let action: () -> Void + + // MARK: Initialization + + init( + viewModel: ViewModel, + isSelected: Binding, + action: @escaping () -> Void + ) { + self.viewModel = viewModel + self._isSelected = isSelected + self.action = action + } + + var body: some View { + subscriptionControlStyle.makeBody( + configuration: .init( + label: .init(titleView), + description: .init(descriptionView), + price: .init(priceView), + isSelected: isSelected, + isActive: viewModel.isActive, + action: action + ) + ) + } + + private var titleView: some View { + Text(viewModel.title) + } + + private var descriptionView: some View { + Text(viewModel.description) + } + + private var priceView: some View { + Text(viewModel.price) + } +} + +// MARK: SubscriptionView.ViewModel + +@available(watchOS, unavailable) +extension SubscriptionView { + struct ViewModel: Identifiable, Equatable, Hashable { + let id: String + let title: String + let price: String + let description: String + let isActive: Bool + } +} + +#if swift(>=5.9) && os(iOS) + #Preview { + VStack { + SubscriptionView( + viewModel: .init( + id: "", + title: "Subscription", + price: "$0.99/month", + description: "Description", + isActive: true + ), + isSelected: .constant(true), + action: {} + ) + SubscriptionView( + viewModel: .init( + id: "", + title: "Subscription", + price: "$0.99/month", + description: "Description", + isActive: false + ), + isSelected: .constant(true), + action: {} + ) + .subscriptionControlStyle(.prominentPicker) + .subscriptionViewTint(.green) + .subscriptionPickerItemBackground(Palette.systemGray5) + } + } +#endif diff --git a/Sources/FlareUI/Makefile b/Sources/FlareUI/Makefile new file mode 100644 index 000000000..1a5286c90 --- /dev/null +++ b/Sources/FlareUI/Makefile @@ -0,0 +1,2 @@ +swiftgen: + swiftgen \ No newline at end of file diff --git a/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/Contents.json b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/Contents.json new file mode 100644 index 000000000..6e965652d --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/dynamic_background.colorset/Contents.json b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/dynamic_background.colorset/Contents.json new file mode 100644 index 000000000..5f3de9e5c --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/dynamic_background.colorset/Contents.json @@ -0,0 +1,68 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "255", + "green" : "255", + "red" : "255" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0", + "green" : "0", + "red" : "0" + } + }, + "idiom" : "universal" + }, + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "255", + "green" : "255", + "red" : "255" + } + }, + "idiom" : "tv" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "38", + "green" : "38", + "red" : "38" + } + }, + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/gray.colorset/Contents.json b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/gray.colorset/Contents.json new file mode 100644 index 000000000..65b9a7b68 --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/gray.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "247", + "green" : "242", + "red" : "242" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "30", + "green" : "28", + "red" : "28" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/system_background.colorset/Contents.json b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/system_background.colorset/Contents.json new file mode 100644 index 000000000..9c2bf753b --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Colors/system_background.colorset/Contents.json @@ -0,0 +1,68 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "255", + "green" : "255", + "red" : "255" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0", + "green" : "0", + "red" : "0" + } + }, + "idiom" : "universal" + }, + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "178", + "green" : "178", + "red" : "178" + } + }, + "idiom" : "tv" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "28", + "green" : "28", + "red" : "28" + } + }, + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Assets.xcassets/Contents.json b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Contents.json b/Sources/FlareUI/Resources/Assets/Media.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/Contents.json b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/Contents.json new file mode 100644 index 000000000..6e965652d --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/Contents.json b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/Contents.json new file mode 100644 index 000000000..cd4b3af1b --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "checkmark.circle.fill.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/checkmark.circle.fill.svg b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/checkmark.circle.fill.svg new file mode 100644 index 000000000..2383ffecb --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/checkmark.imageset/checkmark.circle.fill.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/Contents.json b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/Contents.json new file mode 100644 index 000000000..29d1b4b46 --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "circle.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/circle.svg b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/circle.svg new file mode 100644 index 000000000..9de1a0c3a --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/circle.imageset/circle.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/Contents.json b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/Contents.json new file mode 100644 index 000000000..fb87640df --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/star.svg b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/star.svg new file mode 100644 index 000000000..44f14f52f --- /dev/null +++ b/Sources/FlareUI/Resources/Assets/Media.xcassets/Media/star.imageset/star.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/Sources/FlareUI/Resources/Localization/en.lproj/Localizable.strings b/Sources/FlareUI/Resources/Localization/en.lproj/Localizable.strings new file mode 100644 index 000000000..94b1a967d --- /dev/null +++ b/Sources/FlareUI/Resources/Localization/en.lproj/Localizable.strings @@ -0,0 +1,20 @@ +"store_unavailable.title" = "Store Unavailable"; +"store_unavailable.product.message" = "No in-app purchases are available in the current storefront."; +"store_unavailable.subscription.message" = "The subscription is unavailable in the current storefront."; +"store_button.restore_purchases" = "Restore Missing Purchases"; +"error.default.title" = "Error Occurred"; +"product.subscription.price" = "%@/%@"; +"product.price_description" = "Every %@"; +"subscriptions.renewable.subscription_description" = "Plan auto-renews for %@ until cancelled."; +"subscriptions.renewable.subscription_description_separated" = "Plan auto-renews for %@\nuntil cancelled."; +"subscription.loading.message" = "Loading Subscriptions..."; +"common.terms_of_service" = "Terms of Service"; +"common.privacy_policy" = "Privacy Policy"; +"common.words.and" = "and"; +"policies.unavailable.terms_of_service.title" = "Terms of Service Unavailable"; +"policies.unavailable.privacy_policy.title" = "Privacy Policy Unavailable"; +"policies.unavailable.terms_of_service.message" = "Something went wrong. Try again."; +"policies.unavailable.privacy_policy.message" = "Something went wrong. Try again."; +"common.subscription.status.your_plan" = "Your plan"; +"common.subscription.status.your_current_plan" = "Your current plan"; +"common.subscription.action.subscribe" = "Subscribe"; diff --git a/Sources/FlareUI/Resources/Localization/ru.lproj/Localizable.strings b/Sources/FlareUI/Resources/Localization/ru.lproj/Localizable.strings new file mode 100644 index 000000000..f140e921e --- /dev/null +++ b/Sources/FlareUI/Resources/Localization/ru.lproj/Localizable.strings @@ -0,0 +1,20 @@ +"store_unavailable.title" = "Магазин недоступен"; +"store_unavailable.product.message" = "В текущем магазине недоступны покупки в приложении."; +"store_unavailable.subscription.message" = "Подписка недоступна в текущем магазине."; +"store_button.restore_purchases" = "Восстановить покупки"; +"error.default.title" = "Произошла ошибка"; +"product.subscription.price" = "%@/%@"; +"product.price_description" = "Каждый %@"; +"subscriptions.renewable.subscription_description" = "План автоматически продлевается за %@ до отмены."; +"subscriptions.renewable.subscription_description_separated" = "План автоматически продлевается за %@\nдо отмены."; +"subscription.loading.message" = "Загрузка подписок..."; +"common.terms_of_service" = "Условия использования"; +"common.privacy_policy" = "Политика конфиденциальности"; +"common.words.and" = "и"; +"policies.unavailable.terms_of_service.title" = "Условия предоставления услуг недоступны"; +"policies.unavailable.privacy_policy.title" = "Политика конфиденциальности недоступна"; +"policies.unavailable.terms_of_service.message" = "Что-то пошло не так. Попробуйте снова."; +"policies.unavailable.privacy_policy.message" = "Что-то пошло не так. Попробуйте снова."; +"common.subscription.status.your_plan" = "Ваш план"; +"common.subscription.status.your_current_plan" = "Ваш текущий план"; +"common.subscription.action.subscribe" = "Подписаться"; diff --git a/Sources/FlareUI/swiftgen.yml b/Sources/FlareUI/swiftgen.yml new file mode 100644 index 000000000..0c0a394cd --- /dev/null +++ b/Sources/FlareUI/swiftgen.yml @@ -0,0 +1,24 @@ +input_dir: Resources +output_dir: Classes/Generated +xcassets: + - inputs: Assets/Assets.xcassets + outputs: + templateName: swift5 + output: Colors.swift + params: + publicAccess: false + - inputs: Assets/Media.xcassets + outputs: + templateName: swift5 + output: Media.swift + params: + publicAccess: false + enumName: Media +strings: + inputs: + - Localization/en.lproj/Localizable.strings + outputs: + templateName: structured-swift5 + output: Strings.swift + params: + publicAccess: false \ No newline at end of file diff --git a/Sources/FlareUIMock/Mocks/FlareMock.swift b/Sources/FlareUIMock/Mocks/FlareMock.swift new file mode 100644 index 000000000..364f3cfa6 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/FlareMock.swift @@ -0,0 +1,302 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import Flare +import FlareMock +import Foundation +import Log +import StoreKit + +public final class FlareMock: IFlare { + public init() {} + + public var invokedLogLevelSetter = false + public var invokedLogLevelSetterCount = 0 + public var invokedLogLevel: Log.LogLevel? + public var invokedLogLevelList = [Log.LogLevel]() + public var invokedLogLevelGetter = false + public var invokedLogLevelGetterCount = 0 + public var stubbedLogLevel: Log.LogLevel! + + public var logLevel: Log.LogLevel { + set { + invokedLogLevelSetter = true + invokedLogLevelSetterCount += 1 + invokedLogLevel = newValue + invokedLogLevelList.append(newValue) + } + get { + invokedLogLevelGetter = true + invokedLogLevelGetterCount += 1 + return stubbedLogLevel + } + } + + public var invokedFetchProductIDs = false + public var invokedFetchProductIDsCount = 0 + public var invokedFetchProductIDsParameters: (productIDs: Any, completion: Closure>)? + public var invokedFetchProductIDsParametersList = [(productIDs: Any, completion: Closure>)]() + + public func fetch(productIDs: some Collection, completion: @escaping Closure>) { + invokedFetchProductIDs = true + invokedFetchProductIDsCount += 1 + invokedFetchProductIDsParameters = (productIDs, completion) + invokedFetchProductIDsParametersList.append((productIDs, completion)) + } + + public var invokedFetch = false + public var invokedFetchCount = 0 + public var invokedFetchParameters: (productIDs: Any, Void)? + public var invokedFetchParametersList = [(productIDs: Any, Void)]() + public var stubbedFetchError: Error? + public var stubbedInvokedFetch: [StoreProduct] = [] + + public func fetch(productIDs: some Collection) async throws -> [StoreProduct] { + invokedFetch = true + invokedFetchCount += 1 + invokedFetchParameters = (productIDs, ()) + invokedFetchParametersList.append((productIDs, ())) + if let stubbedFetchError = stubbedFetchError { + throw stubbedFetchError + } + return stubbedInvokedFetch + } + + public var invokedPurchaseProductPromotionalOffer = false + public var invokedPurchaseProductPromotionalOfferCount = 0 + public var invokedPurchaseProductPromotionalOfferParameters: ( + product: StoreProduct, + promotionalOffer: PromotionalOffer?, + completion: Closure> + )? + public var invokedPurchaseProductPromotionalOfferParametersList = [( + product: StoreProduct, + promotionalOffer: PromotionalOffer?, + completion: Closure> + )]() + + public func purchase( + product: StoreProduct, + promotionalOffer: PromotionalOffer?, + completion: @escaping Closure> + ) { + invokedPurchaseProductPromotionalOffer = true + invokedPurchaseProductPromotionalOfferCount += 1 + invokedPurchaseProductPromotionalOfferParameters = (product, promotionalOffer, completion) + invokedPurchaseProductPromotionalOfferParametersList.append((product, promotionalOffer, completion)) + } + + public var invokedPurchase = false + public var invokedPurchaseCount = 0 + public var invokedPurchaseParameters: (product: StoreProduct, promotionalOffer: PromotionalOffer?)? + public var invokedPurchaseParametersList = [(product: StoreProduct, promotionalOffer: PromotionalOffer?)]() + public var stubbedPurchaseError: Error? + public var stubbedPurchase: StoreTransaction! + + public func purchase(product: StoreProduct, promotionalOffer: PromotionalOffer?) async throws -> StoreTransaction { + invokedPurchase = true + invokedPurchaseCount += 1 + invokedPurchaseParameters = (product, promotionalOffer) + invokedPurchaseParametersList.append((product, promotionalOffer)) + if let stubbedPurchaseError = stubbedPurchaseError { + throw stubbedPurchaseError + } + return stubbedPurchase + } + + public var invokedPurchaseProductOptionsPromotionalOffer = false + public var invokedPurchaseProductOptionsPromotionalOfferCount = 0 + public var invokedPurchaseProductOptionsPromotionalOfferParameters: ( + product: StoreProduct, + options: Any, + promotionalOffer: PromotionalOffer?, + completion: SendableClosure> + )? + public var invokedPurchaseProductOptionsPromotionalOfferParametersList = [( + product: StoreProduct, + options: Any, + promotionalOffer: PromotionalOffer?, + completion: SendableClosure> + )]() + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func purchase( + product: StoreProduct, + options: Set, + promotionalOffer: PromotionalOffer?, + completion: @escaping SendableClosure> + ) { + invokedPurchaseProductOptionsPromotionalOffer = true + invokedPurchaseProductOptionsPromotionalOfferCount += 1 + invokedPurchaseProductOptionsPromotionalOfferParameters = (product, options, promotionalOffer, completion) + invokedPurchaseProductOptionsPromotionalOfferParametersList.append((product, options, promotionalOffer, completion)) + } + + public var invokedPurchaseProductOptions = false + public var invokedPurchaseProductOptionsCount = 0 + public var invokedPurchaseProductOptionsParameters: (product: StoreProduct, options: Any, promotionalOffer: PromotionalOffer?)? + public var invokedPurchaseProductOptionsParametersList = [(product: StoreProduct, options: Any, promotionalOffer: PromotionalOffer?)]() + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func purchase( + product: StoreProduct, + options: Set, + promotionalOffer: PromotionalOffer? + ) { + invokedPurchaseProductOptions = true + invokedPurchaseProductOptionsCount += 1 + invokedPurchaseProductOptionsParameters = (product, options, promotionalOffer) + invokedPurchaseProductOptionsParametersList.append((product, options, promotionalOffer)) + } + + public var invokedReceiptCompletion = false + public var invokedReceiptCompletionCount = 0 + public var invokedReceiptCompletionParameters: (completion: Closure>, Void)? + public var invokedReceiptCompletionParametersList = [(completion: Closure>, Void)]() + + public func receipt(completion: @escaping Closure>) { + invokedReceiptCompletion = true + invokedReceiptCompletionCount += 1 + invokedReceiptCompletionParameters = (completion, ()) + invokedReceiptCompletionParametersList.append((completion, ())) + } + + public var invokedReceipt = false + public var invokedReceiptCount = 0 + + public func receipt() { + invokedReceipt = true + invokedReceiptCount += 1 + } + + public var invokedFinishTransaction = false + public var invokedFinishTransactionCount = 0 + public var invokedFinishTransactionParameters: (transaction: StoreTransaction, Void)? + public var invokedFinishTransactionParametersList = [(transaction: StoreTransaction, Void)]() + public var shouldInvokeFinishTransactionCompletion = false + + public func finish(transaction: StoreTransaction, completion: (@Sendable () -> Void)?) { + invokedFinishTransaction = true + invokedFinishTransactionCount += 1 + invokedFinishTransactionParameters = (transaction, ()) + invokedFinishTransactionParametersList.append((transaction, ())) + if shouldInvokeFinishTransactionCompletion { + completion?() + } + } + + public var invokedFinish = false + public var invokedFinishCount = 0 + public var invokedFinishParameters: (transaction: StoreTransaction, Void)? + public var invokedFinishParametersList = [(transaction: StoreTransaction, Void)]() + + public func finish(transaction: StoreTransaction) { + invokedFinish = true + invokedFinishCount += 1 + invokedFinishParameters = (transaction, ()) + invokedFinishParametersList.append((transaction, ())) + } + + public var invokedAddTransactionObserver = false + public var invokedAddTransactionObserverCount = 0 + public var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? + public var invokedAddTransactionObserverParametersList = [(fallbackHandler: Closure>?, Void)]() + + public func addTransactionObserver(fallbackHandler: Closure>?) { + invokedAddTransactionObserver = true + invokedAddTransactionObserverCount += 1 + invokedAddTransactionObserverParameters = (fallbackHandler, ()) + invokedAddTransactionObserverParametersList.append((fallbackHandler, ())) + } + + public var invokedRemoveTransactionObserver = false + public var invokedRemoveTransactionObserverCount = 0 + + public func removeTransactionObserver() { + invokedRemoveTransactionObserver = true + invokedRemoveTransactionObserverCount += 1 + } + + public var invokedCheckEligibility = false + public var invokedCheckEligibilityCount = 0 + public var invokedCheckEligibilityParameters: (productIDs: Set, Void)? + public var invokedCheckEligibilityParametersList = [(productIDs: Set, Void)]() + + public func checkEligibility(productIDs: Set) { + invokedCheckEligibility = true + invokedCheckEligibilityCount += 1 + invokedCheckEligibilityParameters = (productIDs, ()) + invokedCheckEligibilityParametersList.append((productIDs, ())) + } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func purchase( + product _: StoreProduct, + options _: Set, + promotionalOffer _: PromotionalOffer? + ) async throws -> StoreTransaction { + StoreTransaction(paymentTransaction: PaymentTransaction(PaymentTransactionMock())) + } + + public func receipt() async throws -> String { + "" + } + + public func checkEligibility(productIDs _: Set) async throws -> [String: SubscriptionEligibility] { + [:] + } + + public var invokedRestore = false + public var invokedRestoreCount = 0 + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + public func restore() async throws { + invokedRestore = true + invokedRestoreCount += 1 + } + + #if os(iOS) || VISION_OS + public var invokedBeginRefundRequest = false + public var invokedBeginRefundRequestCount = 0 + public var invokedBeginRefundRequestParameters: (productID: String, Void)? + public var invokedBeginRefundRequestParametersList = [(productID: String, Void)]() + public var stubbedBeginRefundRequest: RefundRequestStatus! + + @available(iOS 15.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public func beginRefundRequest(productID: String) async throws -> RefundRequestStatus { + invokedBeginRefundRequest = true + invokedBeginRefundRequestCount += 1 + invokedBeginRefundRequestParameters = (productID, ()) + invokedBeginRefundRequestParametersList.append((productID, ())) + return stubbedBeginRefundRequest + } + + public var invokedPresentCodeRedemptionSheet = false + public var invokedPresentCodeRedemptionSheetCount = 0 + + @available(iOS 14.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public func presentCodeRedemptionSheet() { + invokedPresentCodeRedemptionSheet = true + invokedPresentCodeRedemptionSheetCount += 1 + } + + public var invokedPresentOfferCodeRedeemSheet = false + public var invokedPresentOfferCodeRedeemSheetCount = 0 + + @available(iOS 16.0, *) + @available(macOS, unavailable) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public func presentOfferCodeRedeemSheet() { + invokedPresentOfferCodeRedeemSheet = true + invokedPresentOfferCodeRedeemSheetCount += 1 + } + #endif +} diff --git a/Sources/FlareUIMock/Mocks/ProductPresenterMock.swift b/Sources/FlareUIMock/Mocks/ProductPresenterMock.swift new file mode 100644 index 000000000..7ac95c8f4 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/ProductPresenterMock.swift @@ -0,0 +1,28 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import Foundation + +public final class ProductPresenterMock: IProductPresenter { + public init() {} + + public func viewDidLoad() {} + + public var invokedPurchase = false + public var invokedPurchaseCount = 0 + public var invokedPurchaseParameters: (options: PurchaseOptions?, Void)? + public var invokedPurchaseParametersList = [(options: PurchaseOptions?, Void)]() + public var stubbedPurchase: StoreTransaction = .fake() + + public func purchase(options: PurchaseOptions?) async throws -> StoreTransaction { + invokedPurchase = true + invokedPurchaseCount += 1 + invokedPurchaseParameters = (options, ()) + invokedPurchaseParametersList.append((options, ())) + return stubbedPurchase + } +} diff --git a/Sources/FlareUIMock/Mocks/ProductViewAssemblyMock.swift b/Sources/FlareUIMock/Mocks/ProductViewAssemblyMock.swift new file mode 100644 index 000000000..91c1364d1 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/ProductViewAssemblyMock.swift @@ -0,0 +1,40 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import Foundation + +public final class ProductViewAssemblyMock: IProductViewAssembly { + public init() {} + + public var invokedAssembleId = false + public var invokedAssembleIdCount = 0 + public var invokedAssembleIdParameters: (id: String, Void)? + public var invokedAssembleIdParametersList = [(id: String, Void)]() + public var stubbedAssembleIdResult: ViewWrapper! + + public func assemble(id: String) -> ViewWrapper { + invokedAssembleId = true + invokedAssembleIdCount += 1 + invokedAssembleIdParameters = (id, ()) + invokedAssembleIdParametersList.append((id, ())) + return stubbedAssembleIdResult + } + + public var invokedAssembleStoreProduct = false + public var invokedAssembleStoreProductCount = 0 + public var invokedAssembleStoreProductParameters: (storeProduct: StoreProduct, Void)? + public var invokedAssembleStoreProductParametersList = [(storeProduct: StoreProduct, Void)]() + public var stubbedAssembleStoreProductResult: ViewWrapper! + + public func assemble(storeProduct: StoreProduct) -> ViewWrapper { + invokedAssembleStoreProduct = true + invokedAssembleStoreProductCount += 1 + invokedAssembleStoreProductParameters = (storeProduct, ()) + invokedAssembleStoreProductParametersList.append((storeProduct, ())) + return stubbedAssembleStoreProductResult + } +} diff --git a/Sources/FlareUIMock/Mocks/ProductsPresenterMock.swift b/Sources/FlareUIMock/Mocks/ProductsPresenterMock.swift new file mode 100644 index 000000000..a06a6aef5 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/ProductsPresenterMock.swift @@ -0,0 +1,19 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import Foundation + +public final class ProductsPresenterMock: IProductsPresenter { + public init() {} + + public var invokedViewDidLoad = false + public var invokedViewDidLoadCount = 0 + + public func viewDidLoad() { + invokedViewDidLoad = true + invokedViewDidLoadCount += 1 + } +} diff --git a/Sources/FlareUIMock/Mocks/StoreButtonAssemblyMock.swift b/Sources/FlareUIMock/Mocks/StoreButtonAssemblyMock.swift new file mode 100644 index 000000000..c1f5c4187 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/StoreButtonAssemblyMock.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI + +public final class StoreButtonAssemblyMock: IStoreButtonAssembly { + public init() {} + + public var invokedAssemble = false + public var invokedAssembleCount = 0 + public var invokedAssembleParameters: (storeButtonType: StoreButton, Void)? + public var invokedAssembleParametersList = [(storeButtonType: StoreButton, Void)]() + public var stubbedAssembleResult: ViewWrapper! + + public func assemble(storeButtonType: StoreButton) -> ViewWrapper { + invokedAssemble = true + invokedAssembleCount += 1 + invokedAssembleParameters = (storeButtonType, ()) + invokedAssembleParametersList.append((storeButtonType, ())) + return stubbedAssembleResult + } +} diff --git a/Sources/FlareUIMock/Mocks/StoreButtonsAssemblyMock.swift b/Sources/FlareUIMock/Mocks/StoreButtonsAssemblyMock.swift new file mode 100644 index 000000000..23133f15d --- /dev/null +++ b/Sources/FlareUIMock/Mocks/StoreButtonsAssemblyMock.swift @@ -0,0 +1,25 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import SwiftUI + +public final class StoreButtonsAssemblyMock: IStoreButtonsAssembly { + public init() {} + + public var invokedAssemble = false + public var invokedAssembleCount = 0 + public var invokedAssembleParameters: (storeButtonType: StoreButtonType, Void)? + public var invokedAssembleParametersList = [(storeButtonType: StoreButtonType, Void)]() + public var stubbedAssembleResult: AnyView! + + public func assemble(storeButtonType: StoreButtonType) -> AnyView { + invokedAssemble = true + invokedAssembleCount += 1 + invokedAssembleParameters = (storeButtonType, ()) + invokedAssembleParametersList.append((storeButtonType, ())) + return stubbedAssembleResult + } +} diff --git a/Sources/FlareUIMock/Mocks/SubscriptionsPresenterMock.swift b/Sources/FlareUIMock/Mocks/SubscriptionsPresenterMock.swift new file mode 100644 index 000000000..e0c68cba8 --- /dev/null +++ b/Sources/FlareUIMock/Mocks/SubscriptionsPresenterMock.swift @@ -0,0 +1,60 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import Foundation + +public final class SubscriptionsPresenterMock: ISubscriptionsPresenter { + public init() {} + + public var invokedViewDidLoad = false + public var invokedViewDidLoadCount = 0 + + public func viewDidLoad() { + invokedViewDidLoad = true + invokedViewDidLoadCount += 1 + } + + public var invokedSelectProduct = false + public var invokedSelectProductCount = 0 + public var invokedSelectProductParameters: (id: String, Void)? + public var invokedSelectProductParametersList = [(id: String, Void)]() + + public func selectProduct(with id: String) { + invokedSelectProduct = true + invokedSelectProductCount += 1 + invokedSelectProductParameters = (id, ()) + invokedSelectProductParametersList.append((id, ())) + } + + public var invokedProduct = false + public var invokedProductCount = 0 + public var invokedProductParameters: (id: String, Void)? + public var invokedProductParametersList = [(id: String, Void)]() + public var stubbedProductResult: StoreProduct! + + public func product(withID id: String) -> StoreProduct? { + invokedProduct = true + invokedProductCount += 1 + invokedProductParameters = (id, ()) + invokedProductParametersList.append((id, ())) + return stubbedProductResult + } + + public var invokedSubscribe = false + public var invokedSubscribeCount = 0 + public var invokedSubscribeParameters: (optionsHandler: PurchaseOptionHandler?, Void)? + public var invokedSubscribeParametersList = [(optionsHandler: PurchaseOptionHandler?, Void)]() + public var stubbedSubscribe: StoreTransaction! + + public func subscribe(optionsHandler: PurchaseOptionHandler?) async throws -> StoreTransaction { + invokedSubscribe = true + invokedSubscribeCount += 1 + invokedSubscribeParameters = (optionsHandler, ()) + invokedSubscribeParametersList.append((optionsHandler, ())) + return stubbedSubscribe + } +} diff --git a/Tests/FlareTests/UnitTests/FlareTests.swift b/Tests/FlareTests/UnitTests/FlareTests.swift index 945ece829..a116a97fc 100644 --- a/Tests/FlareTests/UnitTests/FlareTests.swift +++ b/Tests/FlareTests/UnitTests/FlareTests.swift @@ -4,6 +4,7 @@ // @testable import Flare +import FlareMock import StoreKit import XCTest @@ -42,7 +43,7 @@ class FlareTests: XCTestCase { func test_thatFlareFetchesProductsWithGivenProductIDs() { // when - sut.fetch(productIDs: .ids, completion: { _ in }) + sut.fetch(productIDs: Set.ids, completion: { _ in }) // then XCTAssertTrue(iapProviderMock.invokedFetch) @@ -51,14 +52,14 @@ class FlareTests: XCTestCase { func test_thatFlareFetchesProductsWithGivenProductIDs() async throws { // given let productMocks = [ - StoreProduct(skProduct: ProductMock()), - StoreProduct(skProduct: ProductMock()), - StoreProduct(skProduct: ProductMock()), + StoreProduct(ProductMock()), + StoreProduct(ProductMock()), + StoreProduct(ProductMock()), ] iapProviderMock.fetchAsyncResult = productMocks // when - let products = try await sut.fetch(productIDs: .ids) + let products = try await sut.fetch(productIDs: Set.ids) // then XCTAssertEqual(products, productMocks) @@ -69,7 +70,7 @@ class FlareTests: XCTestCase { iapProviderMock.stubbedCanMakePayments = true // when - sut.purchase(product: .fake(skProduct: .fake(id: .productID)), completion: { _ in }) + sut.purchase(product: .fake(productIdentifier: .productID), completion: { _ in }) // then XCTAssertTrue(iapProviderMock.invokedPurchaseWithPromotionalOffer) @@ -81,7 +82,7 @@ class FlareTests: XCTestCase { iapProviderMock.stubbedCanMakePayments = false // when - sut.purchase(product: .fake(skProduct: .fake(id: .productID)), completion: { _ in }) + sut.purchase(product: .fake(productIdentifier: .productID), completion: { _ in }) // then XCTAssertFalse(iapProviderMock.invokedPurchase) @@ -95,7 +96,7 @@ class FlareTests: XCTestCase { // when var transaction: IStoreTransaction? - sut.purchase(product: .fake(skProduct: .fake(id: .productID)), completion: { result in + sut.purchase(product: .fake(productIdentifier: .productID), completion: { result in transaction = result.success }) iapProviderMock.invokedPurchaseParameters?.completion(.success(paymentTransaction)) @@ -113,7 +114,7 @@ class FlareTests: XCTestCase { // when var error: IAPError? - sut.purchase(product: .fake(skProduct: .fake(id: .productID)), completion: { result in + sut.purchase(product: .fake(productIdentifier: .productID), completion: { result in error = result.error }) iapProviderMock.invokedPurchaseParameters?.completion(.failure(errorMock)) @@ -129,7 +130,7 @@ class FlareTests: XCTestCase { iapProviderMock.stubbedAsyncPurchase = StoreTransaction(storeTransaction: StoreTransactionStub()) // when - let iapError: IAPError? = await error(for: { try await sut.purchase(product: .fake(skProduct: .fake(id: .productID))) }) + let iapError: IAPError? = await error(for: { try await sut.purchase(product: .fake(productIdentifier: .productID)) }) // then XCTAssertFalse(iapProviderMock.invokedAsyncPurchase) @@ -144,7 +145,7 @@ class FlareTests: XCTestCase { iapProviderMock.stubbedPurchaseAsyncWithPromotionalOffer = transactionMock // when - let transaction = await value(for: { try await sut.purchase(product: .fake(skProduct: .fake(id: .productID))) }) + let transaction = await value(for: { try await sut.purchase(product: .fake(productIdentifier: .productID)) }) // then XCTAssertTrue(iapProviderMock.invokedPurchaseAsyncWithPromotionalOffer) diff --git a/Tests/FlareTests/UnitTests/Models/PaymentTransactionTests.swift b/Tests/FlareTests/UnitTests/Models/PaymentTransactionTests.swift index b5345c068..5d9743631 100644 --- a/Tests/FlareTests/UnitTests/Models/PaymentTransactionTests.swift +++ b/Tests/FlareTests/UnitTests/Models/PaymentTransactionTests.swift @@ -4,6 +4,7 @@ // @testable import Flare +import FlareMock import struct StoreKit.SKError import class StoreKit.SKPaymentTransaction import enum StoreKit.SKPaymentTransactionState diff --git a/Tests/FlareTests/UnitTests/Models/SKProductTests.swift b/Tests/FlareTests/UnitTests/Models/SKProductTests.swift index b901e091e..77c9aab31 100644 --- a/Tests/FlareTests/UnitTests/Models/SKProductTests.swift +++ b/Tests/FlareTests/UnitTests/Models/SKProductTests.swift @@ -4,6 +4,7 @@ // @testable import Flare +import FlareMock import Foundation import class StoreKit.SKProduct import XCTest @@ -13,7 +14,7 @@ import XCTest final class SKProductTests: XCTestCase { func test_thatSKProductFormatsPriceValueAccoringToLocale() { // given - let product = ProductMock() + let product = SKProductMock() product.stubbedPrice = NSDecimalNumber(value: UInt.price) product.stubbedPriceLocale = Locale(identifier: .localeID) diff --git a/Tests/FlareTests/UnitTests/Providers/CachingProductsProviderDecoratorTests.swift b/Tests/FlareTests/UnitTests/Providers/CachingProductsProviderDecoratorTests.swift index c3c779602..7e16e2e08 100644 --- a/Tests/FlareTests/UnitTests/Providers/CachingProductsProviderDecoratorTests.swift +++ b/Tests/FlareTests/UnitTests/Providers/CachingProductsProviderDecoratorTests.swift @@ -40,7 +40,7 @@ final class CachingProductsProviderDecoratorTests: XCTestCase { func test_thatProviderFetchesCachedProducts_whenFetchCachePolicyIsCachedOrFetch() { // given configurationProviderMock.stubbedFetchCachePolicy = .cachedOrFetch - productProviderMock.stubbedFetchResult = .success([StoreProduct.fake()]) + productProviderMock.stubbedFetchResult = .success([StoreProduct.fake(productIdentifier: .productID)]) // when sut.fetch(productIDs: [.productID], requestID: "", completion: { _ in }) diff --git a/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift b/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift index ac9ed1a5f..3db90c719 100644 --- a/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift +++ b/Tests/FlareTests/UnitTests/Providers/IAPProviderTests.swift @@ -4,6 +4,7 @@ // @testable import Flare +import FlareMock import StoreKit import XCTest @@ -64,17 +65,17 @@ class IAPProviderTests: XCTestCase { try AvailabilityChecker.iOS15APINotAvailableOrSkipTest() // when - sut.fetch(productIDs: .productIDs, completion: { _ in }) + sut.fetch(productIDs: Set.productIDs, completion: { _ in }) // then let parameters = try XCTUnwrap(productProviderMock.invokedFetchParameters) - XCTAssertEqual(parameters.productIDs, .productIDs) + XCTAssertEqual(parameters.productIDs as? Set, Set.productIDs) XCTAssertTrue(!parameters.requestID.isEmpty) } func test_thatIAPProviderPurchasesProduct() throws { // when - sut.purchase(product: .fake(skProduct: .fake(id: .productID)), completion: { _ in }) + sut.purchase(product: .fake(productIdentifier: .productID), completion: { _ in }) // then XCTAssertTrue(purchaseProvider.invokedPurchase) @@ -131,11 +132,11 @@ class IAPProviderTests: XCTestCase { try AvailabilityChecker.iOS15APINotAvailableOrSkipTest() // given - let productsMock = [0 ... 2].map { _ in StoreProduct(SK1StoreProduct(ProductMock())) } + let productsMock = [0 ... 2].map { _ in StoreProduct(SK1StoreProduct(SKProductMock())) } productProviderMock.stubbedFetchResult = .success(productsMock) // when - let products = try await sut.fetch(productIDs: .productIDs) + let products = try await sut.fetch(productIDs: Set.productIDs) // then XCTAssertEqual(productsMock.count, products.count) @@ -148,7 +149,7 @@ class IAPProviderTests: XCTestCase { productProviderMock.stubbedFetchResult = .failure(IAPError.unknown) // when - let errorResult: Error? = await error(for: { try await sut.fetch(productIDs: .productIDs) }) + let errorResult: Error? = await error(for: { try await sut.fetch(productIDs: Set.productIDs) }) // then XCTAssertEqual(errorResult as? NSError, IAPError.unknown as NSError) @@ -156,12 +157,12 @@ class IAPProviderTests: XCTestCase { func test_thatIAPProviderReturnsError_whenAddingPaymentFailed() { // given - productProviderMock.stubbedFetchResult = .success([StoreProduct(SK1StoreProduct(ProductMock()))]) + productProviderMock.stubbedFetchResult = .success([StoreProduct(SK1StoreProduct(SKProductMock()))]) purchaseProvider.stubbedPurchaseCompletionResult = (.failure(.unknown), ()) // when var error: Error? - sut.purchase(product: .fake(skProduct: .fake(id: .productID))) { error = $0.error } + sut.purchase(product: .fake(productIdentifier: .productID)) { error = $0.error } // then XCTAssertEqual(error as? NSError, IAPError.unknown as NSError) @@ -173,7 +174,7 @@ class IAPProviderTests: XCTestCase { // when var error: Error? - sut.purchase(product: .fake(skProduct: .fake(id: .productID))) { error = $0.error } + sut.purchase(product: .fake(productIdentifier: .productID)) { error = $0.error } // then XCTAssertEqual(error as? NSError, IAPError.unknown as NSError) diff --git a/Tests/FlareTests/UnitTests/Providers/ProductProviderTests.swift b/Tests/FlareTests/UnitTests/Providers/ProductProviderTests.swift index 497601be2..9b3bba729 100644 --- a/Tests/FlareTests/UnitTests/Providers/ProductProviderTests.swift +++ b/Tests/FlareTests/UnitTests/Providers/ProductProviderTests.swift @@ -47,7 +47,7 @@ final class ProductProviderTests: XCTestCase { response.stubbedInvokedInvalidProductsIdentifiers = [.productID] // when - sut.fetch(productIDs: .productIDs, requestID: .requestID, completion: completionHandler) + sut.fetch(productIDs: Set.productIDs, requestID: .requestID, completion: completionHandler) sut.productsRequest(request, didReceive: response) // then @@ -66,7 +66,7 @@ final class ProductProviderTests: XCTestCase { let response = ProductResponseMock() // when - sut.fetch(productIDs: .productIDs, requestID: .requestID, completion: completionHandler) + sut.fetch(productIDs: Set.productIDs, requestID: .requestID, completion: completionHandler) sut.productsRequest(request, didReceive: response) // then @@ -81,7 +81,7 @@ final class ProductProviderTests: XCTestCase { let errorStub = IAPError.unknown // when - sut.fetch(productIDs: .productIDs, requestID: .requestID, completion: completionHandler) + sut.fetch(productIDs: Set.productIDs, requestID: .requestID, completion: completionHandler) sut.request(request, didFailWithError: errorStub) // then diff --git a/Tests/FlareTests/UnitTests/Providers/PurchaseProviderTests.swift b/Tests/FlareTests/UnitTests/Providers/PurchaseProviderTests.swift index dee0878b7..eba48d7d5 100644 --- a/Tests/FlareTests/UnitTests/Providers/PurchaseProviderTests.swift +++ b/Tests/FlareTests/UnitTests/Providers/PurchaseProviderTests.swift @@ -4,6 +4,7 @@ // @testable import Flare +import FlareMock import StoreKit import StoreKitTest import XCTest @@ -41,7 +42,7 @@ final class PurchaseProviderTests: XCTestCase { func test_thatPurchaseProviderReturnsPaymentTransaction_whenSK1ProductExist() { // given - let productMock = StoreProduct(skProduct: ProductMock()) + let productMock = StoreProduct(ProductMock()) let paymentTransaction = SKPaymentTransaction() let storeTransaction = StoreTransaction(paymentTransaction: PaymentTransaction(paymentTransaction)) diff --git a/Tests/FlareTests/UnitTests/Providers/SortingProductsProviderDecoratorTests.swift b/Tests/FlareTests/UnitTests/Providers/SortingProductsProviderDecoratorTests.swift new file mode 100644 index 000000000..75ea23512 --- /dev/null +++ b/Tests/FlareTests/UnitTests/Providers/SortingProductsProviderDecoratorTests.swift @@ -0,0 +1,79 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import Flare +import FlareMock +import XCTest + +// MARK: - SortingProductsProviderDecoratorTests + +final class SortingProductsProviderDecoratorTests: XCTestCase { + // MARK: Properties + + private var productProviderMock: ProductProviderMock! + + private var sut: SortingProductsProviderDecorator! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + productProviderMock = ProductProviderMock() + sut = SortingProductsProviderDecorator(productProvider: productProviderMock) + } + + override func tearDown() { + productProviderMock = nil + sut = nil + super.tearDown() + } + + // MARK: Tests + + func test_ProductProviderSortsSetItems_whenFetchProducts() { + test_sort(collection: Set.productIDs) + } + + func test_ProductProviderSortsArrayItems_whenFetchProducts() { + test_sort(collection: Array.productIDs) + } + + // MARK: Private + + private func test_sort(collection: some Collection) { + // given + let ids = collection + let products: [StoreProduct] = ids + .map { .fake(productIdentifier: $0) } + .shuffled() + productProviderMock.stubbedFetchResult = .success(products) + + // when + var resultProducts: [StoreProduct] = [] + sut.fetch(productIDs: ids, requestID: .requestID) { result in + if case let .success(products) = result { + resultProducts = products + } + } + + // then + XCTAssertEqual(ids.count, resultProducts.count) + XCTAssertEqual(Array(ids), resultProducts.map(\.productIdentifier)) + } +} + +// MARK: - Constants + +private extension String { + static let requestID = "requestID" +} + +private extension Array where Element == String { + static let productIDs: [Element] = ["1", "2", "3"] +} + +private extension Set where Element == String { + static let productIDs: Set = .init(arrayLiteral: "1", "2", "3") +} diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Fakes/SKProduct+Fake.swift b/Tests/FlareTests/UnitTests/TestHelpers/Fakes/SKProduct+Fake.swift index a748b6270..d85f09fcf 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Fakes/SKProduct+Fake.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Fakes/SKProduct+Fake.swift @@ -1,14 +1,15 @@ // // Flare -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // +import FlareMock import Foundation import StoreKit extension SKProduct { static func fake(id: String) -> SKProduct { - let product = ProductMock() + let product = SKProductMock() product.stubbedProductIdentifier = id return product } diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Fakes/StoreProduct+Fake.swift b/Tests/FlareTests/UnitTests/TestHelpers/Fakes/StoreProduct+Fake.swift deleted file mode 100644 index 4f93ff255..000000000 --- a/Tests/FlareTests/UnitTests/TestHelpers/Fakes/StoreProduct+Fake.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Flare -// Copyright © 2023 Space Code. All rights reserved. -// - -import Flare -import StoreKit - -extension StoreProduct { - static func fake(skProduct: SKProduct = ProductMock()) -> StoreProduct { - StoreProduct(skProduct: skProduct) - } -} diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Helpers/PurchaseManagerTestHelper.swift b/Tests/FlareTests/UnitTests/TestHelpers/Helpers/PurchaseManagerTestHelper.swift index 1d67b7f43..63201f958 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Helpers/PurchaseManagerTestHelper.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Helpers/PurchaseManagerTestHelper.swift @@ -1,9 +1,10 @@ // // Flare -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // @testable import Flare +import FlareMock import StoreKit enum PurchaseManagerTestHelper { @@ -23,7 +24,7 @@ enum PurchaseManagerTestHelper { } static func makeProduct(with productIdentifier: String) -> SKProduct { - let product = ProductMock() + let product = SKProductMock() product.stubbedProductIdentifier = productIdentifier return product } diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift index c1f84b1f6..296b91b41 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/IAPProviderMock.swift @@ -19,10 +19,10 @@ final class IAPProviderMock: IIAPProvider { var invokedFetch = false var invokedFetchCount = 0 - var invokedFetchParameters: (productIDs: Set, completion: Closure>)? - var invokedFetchParametersList = [(productIDs: Set, completion: Closure>)]() + var invokedFetchParameters: (productIDs: Any, completion: Closure>)? + var invokedFetchParametersList = [(productIDs: Any, completion: Closure>)]() - func fetch(productIDs: Set, completion: @escaping Closure>) { + func fetch(productIDs: some Collection, completion: @escaping Closure>) { invokedFetch = true invokedFetchCount += 1 invokedFetchParameters = (productIDs, completion) @@ -84,10 +84,10 @@ final class IAPProviderMock: IIAPProvider { var invokedAddTransactionObserver = false var invokedAddTransactionObserverCount = 0 - var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? - var invokedAddTransactionObserverParametersList = [(fallbackHandler: Closure>?, Void)]() + var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? + var invokedAddTransactionObserverParametersList = [(fallbackHandler: Closure>?, Void)]() - func addTransactionObserver(fallbackHandler: Closure>?) { + func addTransactionObserver(fallbackHandler: Closure>?) { invokedAddTransactionObserver = true invokedAddTransactionObserverCount += 1 invokedAddTransactionObserverParameters = (fallbackHandler, ()) @@ -104,11 +104,11 @@ final class IAPProviderMock: IIAPProvider { var invokedFetchAsync = false var invokedFetchAsyncCount = 0 - var invokedFetchAsyncParameters: (productIDs: Set, Void)? - var invokedFetchAsyncParametersList = [(productIDs: Set, Void)]() + var invokedFetchAsyncParameters: (productIDs: Any, Void)? + var invokedFetchAsyncParametersList = [(productIDs: Any, Void)]() var fetchAsyncResult: [StoreProduct] = [] - func fetch(productIDs: Set) async throws -> [StoreProduct] { + func fetch(productIDs: some Collection) async throws -> [StoreProduct] { invokedFetchAsync = true invokedFetchAsyncCount += 1 invokedFetchAsyncParameters = (productIDs, ()) @@ -296,4 +296,7 @@ final class IAPProviderMock: IIAPProvider { invokedPresentOfferCodeRedeemSheet = true invokedPresentOfferCodeRedeemSheetCount += 1 } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws {} } diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PaymentTransactionMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PaymentTransactionMock.swift deleted file mode 100644 index efae4bd7d..000000000 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PaymentTransactionMock.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// Flare -// Copyright © 2023 Space Code. All rights reserved. -// - -import StoreKit - -final class PaymentTransactionMock: SKPaymentTransaction { - var invokedTransactionState = false - var invokedTransactionStateCount = 0 - var stubbedTransactionState: SKPaymentTransactionState! - - override var transactionState: SKPaymentTransactionState { - stubbedTransactionState - } - - var invokedTransactionIndentifier = false - var invokedTransactionIndentifierCount = 0 - var stubbedTransactionIndentifier: String? - - override var transactionIdentifier: String? { - invokedTransactionIndentifier = true - invokedTransactionStateCount += 1 - return stubbedTransactionIndentifier - } - - var invokedPayment = false - var invokedPaymentCount = 0 - var stubbedPayment: SKPayment! - - override var payment: SKPayment { - invokedPayment = true - invokedPaymentCount += 1 - return stubbedPayment - } - - var stubbedOriginal: SKPaymentTransaction? - override var original: SKPaymentTransaction? { - stubbedOriginal - } - - var stubbedError: Error? - override var error: Error? { - stubbedError - } -} diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductProviderMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductProviderMock.swift index 489118ce7..16bd75bfd 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductProviderMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductProviderMock.swift @@ -9,11 +9,11 @@ import StoreKit final class ProductProviderMock: IProductProvider { var invokedFetch = false var invokedFetchCount = 0 - var invokedFetchParameters: (productIDs: Set, requestID: String, completion: ProductsHandler)? - var invokedFetchParamtersList = [(productIDs: Set, requestID: String, completion: ProductsHandler)]() + var invokedFetchParameters: (productIDs: Any, requestID: String, completion: ProductsHandler)? + var invokedFetchParamtersList = [(productIDs: Any, requestID: String, completion: ProductsHandler)]() var stubbedFetchResult: Result<[StoreProduct], IAPError>? - func fetch(productIDs: Set, requestID: String, completion: @escaping ProductsHandler) { + func fetch(productIDs: some Collection, requestID: String, completion: @escaping ProductsHandler) { invokedFetch = true invokedFetchCount += 1 invokedFetchParameters = (productIDs, requestID, completion) @@ -26,12 +26,12 @@ final class ProductProviderMock: IProductProvider { var invokedAsyncFetch = false var invokedAsyncFetchCount = 0 - var invokedAsyncFetchParameters: (productIDs: Set, Void)? - var invokedAsyncFetchParamtersList = [(productIDs: Set, Void)]() + var invokedAsyncFetchParameters: (productIDs: Any, Void)? + var invokedAsyncFetchParamtersList = [(productIDs: Any, Void)]() var stubbedAsyncFetchResult: Result<[StoreProduct], Error>? @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) - func fetch(productIDs: Set) async throws -> [StoreProduct] { + func fetch(productIDs: some Collection) async throws -> [StoreProduct] { invokedAsyncFetch = true invokedAsyncFetchCount += 1 invokedAsyncFetchParameters = (productIDs, ()) diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift index 3941055b7..f58fd3121 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/PurchaseProviderMock.swift @@ -22,10 +22,10 @@ final class PurchaseProviderMock: IPurchaseProvider { var invokedAddTransactionObserver = false var invokedAddTransactionObserverCount = 0 - var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? - var invokedAddTransactionObserverParametersList = [(fallbackHandler: Closure>?, Void)]() + var invokedAddTransactionObserverParameters: (fallbackHandler: Closure>?, Void)? + var invokedAddTransactionObserverParametersList = [(fallbackHandler: Closure>?, Void)]() - func addTransactionObserver(fallbackHandler: Closure>?) { + func addTransactionObserver(fallbackHandler: Closure>?) { invokedAddTransactionObserver = true invokedAddTransactionObserverCount += 1 invokedAddTransactionObserverParameters = (fallbackHandler, ()) @@ -80,4 +80,7 @@ final class PurchaseProviderMock: IPurchaseProvider { completion(result.0) } } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func restore() async throws {} } diff --git a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductMock.swift b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/SKProductMock.swift similarity index 94% rename from Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductMock.swift rename to Tests/FlareTests/UnitTests/TestHelpers/Mocks/SKProductMock.swift index 9d765f02e..74e27fcb4 100644 --- a/Tests/FlareTests/UnitTests/TestHelpers/Mocks/ProductMock.swift +++ b/Tests/FlareTests/UnitTests/TestHelpers/Mocks/SKProductMock.swift @@ -5,7 +5,7 @@ import StoreKit -final class ProductMock: SKProduct { +final class SKProductMock: SKProduct { var invokedProductIdentifier = false var invokedProductIdentifierCount = 0 var stubbedProductIdentifier: String = "product_id" diff --git a/Tests/FlareUITests/UnitTests/Core/Extensions/ArrayExtensionsTests.swift b/Tests/FlareUITests/UnitTests/Core/Extensions/ArrayExtensionsTests.swift new file mode 100644 index 000000000..d2ea3a48c --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Core/Extensions/ArrayExtensionsTests.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import XCTest + +final class ArrayExtensionsTests: XCTestCase { + func test_thatArrayRemovesDuplicates() { + // given + let array = [10, 10, 9, 1, 3, 3, 7, 8, 7, 7, 7] + + // when + let filteredArray = array.removingDuplicates() + + // then + XCTAssertEqual(filteredArray, [10, 9, 1, 3, 7, 8]) + } +} diff --git a/Tests/FlareUITests/UnitTests/Core/SubscriptionPriceViewModelFactoryTests.swift b/Tests/FlareUITests/UnitTests/Core/SubscriptionPriceViewModelFactoryTests.swift new file mode 100644 index 000000000..1b404acac --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Core/SubscriptionPriceViewModelFactoryTests.swift @@ -0,0 +1,94 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import FlareMock +@testable import FlareUI +import XCTest + +// MARK: - SubscriptionPriceViewModelFactoryTests + +final class SubscriptionPriceViewModelFactoryTests: XCTestCase { + // MARK: Properties + + private var dateComponentsFormatterMock: DateComponentsFormatterMock! + private var subscriptionDateComponentsFactoryMock: SubscriptionDateComponentsFactoryMock! + + private var sut: SubscriptionPriceViewModelFactory! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + dateComponentsFormatterMock = DateComponentsFormatterMock() + subscriptionDateComponentsFactoryMock = SubscriptionDateComponentsFactoryMock() + sut = SubscriptionPriceViewModelFactory( + dateFormatter: dateComponentsFormatterMock, + subscriptionDateComponentsFactory: subscriptionDateComponentsFactoryMock + ) + } + + override func tearDown() { + dateComponentsFormatterMock = nil + subscriptionDateComponentsFactoryMock = nil + sut = nil + super.tearDown() + } + + // MARK: Tests + + func test_thatFactoryMakesAProduct_whenProductIsConsumable() { + // given + let product: StoreProduct = .fake(productType: .consumable) + + // when + let viewModel = sut.make(product, format: .short) + + // then + XCTAssertEqual(viewModel, product.localizedPriceString) + } + + func test_thatFactoryMakesProductWithCompactStyle_whenProductTypeIsRenewableSubscription() { + // given + subscriptionDateComponentsFactoryMock.stubbedDateComponentsResult = DateComponents(day: 1) + dateComponentsFormatterMock.stubbedStringResult = "1 month" + + let product: StoreProduct = .fake( + localizedPriceString: .price, + productType: .autoRenewableSubscription, + subscriptionPeriod: .init(value: 1, unit: .day) + ) + + // when + let viewModel = sut.make(product, format: .short) + + // then + XCTAssertEqual(viewModel, "10 $") + } + + func test_thatFactoryMakesProductWithLargeStyle_whenProductTypeIsRenewableSubscription() { + // given + subscriptionDateComponentsFactoryMock.stubbedDateComponentsResult = DateComponents(day: 1) + dateComponentsFormatterMock.stubbedStringResult = "1 month" + + let product: StoreProduct = .fake( + localizedPriceString: .price, + productType: .autoRenewableSubscription, + subscriptionPeriod: .init(value: 1, unit: .day) + ) + + // when + let viewModel = sut.make(product, format: .full) + + // then + XCTAssertEqual(viewModel, "10 $/month") + } +} + +// MARK: - Constants + +private extension String { + static let price = "10 $" +} diff --git a/Tests/FlareUITests/UnitTests/Fakes/SubscriptionView.ViewModel+Fake.swift b/Tests/FlareUITests/UnitTests/Fakes/SubscriptionView.ViewModel+Fake.swift new file mode 100644 index 000000000..61bfeb930 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Fakes/SubscriptionView.ViewModel+Fake.swift @@ -0,0 +1,20 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import Foundation + +@available(watchOS, unavailable) +extension SubscriptionView.ViewModel { + static func fake(id: String? = nil) -> SubscriptionView.ViewModel { + SubscriptionView.ViewModel( + id: id ?? UUID().uuidString, + title: "Title", + price: "5,99$", + description: "Description", + isActive: true + ) + } +} diff --git a/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+.swift b/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+.swift new file mode 100644 index 000000000..3e1c6feef --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+.swift @@ -0,0 +1,35 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import XCTest + +extension XCTestCase { + func value(for closure: () async throws -> U) async -> U? { + do { + let value = try await closure() + return value + } catch { + return nil + } + } + + func error(for closure: () async throws -> U) async -> T? { + do { + _ = try await closure() + return nil + } catch { + return error as? T + } + } + + func result(for closure: () async throws -> U) async -> Result { + do { + let value = try await closure() + return .success(value) + } catch { + return .failure(error as! T) + } + } +} diff --git a/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+Wait.swift b/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+Wait.swift new file mode 100644 index 000000000..d2f99c8b5 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Helpers/XCTestCase+Wait.swift @@ -0,0 +1,22 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import XCTest + +extension XCTestCase { + func wait( + _ condition: @escaping @autoclosure () -> (Bool), + timeout: TimeInterval = 10 + ) { + wait( + for: [ + XCTNSPredicateExpectation( + predicate: NSPredicate(block: { _, _ in condition() }), object: nil + ), + ], + timeout: timeout + ) + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/DateComponentsFormatterMock.swift b/Tests/FlareUITests/UnitTests/Mocks/DateComponentsFormatterMock.swift new file mode 100644 index 000000000..645034dc2 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/DateComponentsFormatterMock.swift @@ -0,0 +1,45 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import Foundation + +final class DateComponentsFormatterMock: IDateComponentsFormatter { + var invokedAllowedUnitsSetter = false + var invokedAllowedUnitsSetterCount = 0 + var invokedAllowedUnits: NSCalendar.Unit? + var invokedAllowedUnitsList = [NSCalendar.Unit]() + var invokedAllowedUnitsGetter = false + var invokedAllowedUnitsGetterCount = 0 + var stubbedAllowedUnits: NSCalendar.Unit! + + var allowedUnits: NSCalendar.Unit { + set { + invokedAllowedUnitsSetter = true + invokedAllowedUnitsSetterCount += 1 + invokedAllowedUnits = newValue + invokedAllowedUnitsList.append(newValue) + } + get { + invokedAllowedUnitsGetter = true + invokedAllowedUnitsGetterCount += 1 + return stubbedAllowedUnits + } + } + + var invokedString = false + var invokedStringCount = 0 + var invokedStringParameters: (from: DateComponents, Void)? + var invokedStringParametersList = [(from: DateComponents, Void)]() + var stubbedStringResult: String! + + func string(from: DateComponents) -> String? { + invokedString = true + invokedStringCount += 1 + invokedStringParameters = (from, ()) + invokedStringParametersList.append((from, ())) + return stubbedStringResult + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/ProductFetcherMock.swift b/Tests/FlareUITests/UnitTests/Mocks/ProductFetcherMock.swift new file mode 100644 index 000000000..4b7dcb476 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/ProductFetcherMock.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import Foundation + +final class ProductFetcherMock: IProductFetcherStrategy { + var invokedProduct = false + var invokedProductCount = 0 + var stubbedThrowProduct: Error? + var stubbedProduct: StoreProduct! + + func product() async throws -> StoreProduct { + invokedProduct = true + invokedProductCount += 1 + if let stubbedThrowProduct = stubbedThrowProduct { + throw stubbedThrowProduct + } + return stubbedProduct + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/ProductPurchaseServiceMock.swift b/Tests/FlareUITests/UnitTests/Mocks/ProductPurchaseServiceMock.swift new file mode 100644 index 000000000..daf1a08aa --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/ProductPurchaseServiceMock.swift @@ -0,0 +1,27 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI + +final class ProductPurchaseServiceMock: IProductPurchaseService { + var invokedPurchase = false + var invokedPurchaseCount = 0 + var invokedPurchaseParameters: (product: StoreProduct, options: PurchaseOptions?)? + var invokedPurchaseParametersList = [(product: StoreProduct, options: PurchaseOptions?)]() + var stubbedPurchaseError: Error? + var stubbedPurchase: StoreTransaction = .fake() + + func purchase(product: StoreProduct, options: PurchaseOptions?) async throws -> StoreTransaction { + invokedPurchase = true + invokedPurchaseCount += 1 + invokedPurchaseParameters = (product, options) + invokedPurchaseParametersList.append((product, options)) + if let stubbedPurchaseError { + throw stubbedPurchaseError + } + return stubbedPurchase + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/SubscriptionDateComponentsFactoryMock.swift b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionDateComponentsFactoryMock.swift new file mode 100644 index 000000000..57820de3b --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionDateComponentsFactoryMock.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import Foundation + +final class SubscriptionDateComponentsFactoryMock: ISubscriptionDateComponentsFactory { + var invokedDateComponents = false + var invokedDateComponentsCount = 0 + var invokedDateComponentsParameters: (subscription: SubscriptionPeriod, Void)? + var invokedDateComponentsParametersList = [(subscription: SubscriptionPeriod, Void)]() + var stubbedDateComponentsResult: DateComponents! + + func dateComponents(for subscription: SubscriptionPeriod) -> DateComponents { + invokedDateComponents = true + invokedDateComponentsCount += 1 + invokedDateComponentsParameters = (subscription, ()) + invokedDateComponentsParametersList.append((subscription, ())) + return stubbedDateComponentsResult + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/SubscriptionPriceViewModelFactoryMock.swift b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionPriceViewModelFactoryMock.swift new file mode 100644 index 000000000..ecf02d419 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionPriceViewModelFactoryMock.swift @@ -0,0 +1,37 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI + +final class SubscriptionPriceViewModelFactoryMock: ISubscriptionPriceViewModelFactory { + var invokedMake = false + var invokedMakeCount = 0 + var invokedMakeParameters: (product: StoreProduct, format: PriceDisplayFormat)? + var invokedMakeParametersList = [(product: StoreProduct, format: PriceDisplayFormat)]() + var stubbedMakeResult: String! = "" + + func make(_ product: StoreProduct, format: PriceDisplayFormat) -> String { + invokedMake = true + invokedMakeCount += 1 + invokedMakeParameters = (product, format) + invokedMakeParametersList.append((product, format)) + return stubbedMakeResult + } + + var invokedPeriod = false + var invokedPeriodCount = 0 + var invokedPeriodParameters: (product: StoreProduct, Void)? + var invokedPeriodParametersList = [(product: StoreProduct, Void)]() + var stubbedPeriodResult: String! + + func period(from product: StoreProduct) -> String? { + invokedPeriod = true + invokedPeriodCount += 1 + invokedPeriodParameters = (product, ()) + invokedPeriodParametersList.append((product, ())) + return stubbedPeriodResult + } +} diff --git a/Tests/FlareUITests/UnitTests/Mocks/SubscriptionsViewModelViewFactoryMock.swift b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionsViewModelViewFactoryMock.swift new file mode 100644 index 000000000..bb7e345f3 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Mocks/SubscriptionsViewModelViewFactoryMock.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI + +@available(watchOS, unavailable) +final class SubscriptionsViewModelViewFactoryMock: ISubscriptionsViewModelViewFactory { + var invokedMake = false + var invokedMakeCount = 0 + var invokedMakeParameters: (products: [StoreProduct], Void)? + var invokedMakeParametersList = [(products: [StoreProduct], Void)]() + var stubbedMake: [SubscriptionView.ViewModel] = [] + + func make(_ products: [StoreProduct]) async throws -> [SubscriptionView.ViewModel] { + invokedMake = true + invokedMakeCount += 1 + invokedMakeParameters = (products, ()) + invokedMakeParametersList.append((products, ())) + return stubbedMake + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Product/ProductPresenterTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductPresenterTests.swift new file mode 100644 index 000000000..3cf6091b1 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductPresenterTests.swift @@ -0,0 +1,88 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import FlareMock +@testable import FlareUI +import FlareUIMock +import XCTest + +final class ProductPresenterTests: XCTestCase { + // MARK: Properties + + private var purchaseServiceMock: ProductPurchaseServiceMock! + private var productFetcherMock: ProductFetcherMock! + private var viewModelMock: WrapperViewModel! + + private var sut: ProductPresenter! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + purchaseServiceMock = ProductPurchaseServiceMock() + productFetcherMock = ProductFetcherMock() + sut = ProductPresenter( + productFetcher: productFetcherMock, + purchaseService: purchaseServiceMock + ) + viewModelMock = WrapperViewModel(model: ProductViewModel(state: .loading, presenter: sut)) + sut.viewModel = viewModelMock + } + + override func tearDown() { + sut = nil + productFetcherMock = nil + purchaseServiceMock = nil + viewModelMock = nil + super.tearDown() + } + + // MARK: Tests + + func test_thatPresenterFetchesProduct_whenViewDidLoad() async { + // given + let productFake = StoreProduct.fake() + productFetcherMock.stubbedProduct = productFake + + // when + sut.viewDidLoad() + + // then + wait(self.viewModelMock.model.state == .product(productFake)) + } + + func test_thatPresenterDisplaysAnError_whenViewDidLoad() async { + // given + productFetcherMock.stubbedThrowProduct = IAPError.unknown + + // when + sut.viewDidLoad() + + // then + wait(self.viewModelMock.model.state == .error(.unknown)) + } + + func test_thatPresenterThrowsAnErrorIfProductIsMissing_whenPurchase() async throws { + // when + let error: Error? = await error(for: { try await sut.purchase(options: nil) }) + + // then + let iapError = try XCTUnwrap(error as? IAPError) + XCTAssertEqual(iapError, .unknown) + } + + func test_thatPresenterFinishesTransaction_whenPurchase() async throws { + // given + viewModelMock.model = .init(state: .product(.fake()), presenter: sut) + purchaseServiceMock.stubbedPurchase = .fake() + + // when + _ = try await sut.purchase(options: nil) + + // then + XCTAssertEqual(purchaseServiceMock.invokedPurchaseCount, 1) + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Product/ProductStrategyTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductStrategyTests.swift new file mode 100644 index 000000000..4c451cdc5 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductStrategyTests.swift @@ -0,0 +1,66 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import FlareMock +@testable import FlareUI +import FlareUIMock +import XCTest + +final class ProductStrategyTests: XCTestCase { + // MARK: Properties + + private var iapMock: FlareMock! + + private var sut: ProductStrategy! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + iapMock = FlareMock() + } + + override func tearDown() { + iapMock = nil + super.tearDown() + } + + // MARK: Tests + + func test_strategyReturnsProduct() async throws { + // given + let productFake = StoreProduct.fake() + let sut = prepareSut(type: .product(productFake)) + + // when + let product = try await sut.product() + + // then + XCTAssertEqual(product, productFake) + XCTAssertEqual(iapMock.invokedFetchCount, 0) + } + + func test_strategyFetchesProduct() async throws { + // given + let productFake = StoreProduct.fake() + iapMock.stubbedInvokedFetch = [productFake] + + let sut = prepareSut(type: .productID(productFake.productIdentifier)) + + // when + let product = try await sut.product() + + // then + XCTAssertEqual(product, productFake) + XCTAssertEqual(iapMock.invokedFetchCount, 1) + } + + // MARK: Private + + private func prepareSut(type: ProductViewType) -> ProductStrategy { + ProductStrategy(type: type, iap: iapMock) + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Product/ProductViewModelFactoryTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductViewModelFactoryTests.swift new file mode 100644 index 000000000..473ff8547 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Product/ProductViewModelFactoryTests.swift @@ -0,0 +1,103 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import FlareMock +@testable import FlareUI +import XCTest + +// MARK: - ProductViewModelFactoryTests + +final class ProductViewModelFactoryTests: XCTestCase { + // MARK: Properties + + private var subscriptionPriceViewModelFactoryMock: SubscriptionPriceViewModelFactoryMock! + + private var sut: ProductViewModelFactory! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + subscriptionPriceViewModelFactoryMock = SubscriptionPriceViewModelFactoryMock() + sut = ProductViewModelFactory( + subscriptionPriceViewModelFactory: subscriptionPriceViewModelFactoryMock + ) + } + + override func tearDown() { + subscriptionPriceViewModelFactoryMock = nil + sut = nil + super.tearDown() + } + + // MARK: Tests + + func test_thatFactoryMakesAProduct_whenProductIsConsumable() { + // given + subscriptionPriceViewModelFactoryMock.stubbedMakeResult = .price + + let product: StoreProduct = .fake(localizedPriceString: .price, productType: .consumable) + + // when + let viewModel = sut.make(product, style: .compact) + + // then + XCTAssertEqual(viewModel.id, product.productIdentifier) + XCTAssertEqual(viewModel.title, product.localizedTitle) + XCTAssertEqual(viewModel.description, product.localizedDescription) + XCTAssertEqual(viewModel.price, product.localizedPriceString) + } + + func test_thatFactoryMakesProductWithCompactStyle_whenProductTypeIsRenewableSubscription() { + // given + subscriptionPriceViewModelFactoryMock.stubbedMakeResult = .price + subscriptionPriceViewModelFactoryMock.stubbedPeriodResult = "Month" + + let product: StoreProduct = .fake( + localizedPriceString: .price, + productType: .autoRenewableSubscription, + subscriptionPeriod: .init(value: 1, unit: .day) + ) + + // when + let viewModel = sut.make(product, style: .compact) + + // then + XCTAssertEqual(viewModel.id, product.productIdentifier) + XCTAssertEqual(viewModel.title, product.localizedTitle) + XCTAssertEqual(viewModel.description, product.localizedDescription) + XCTAssertEqual(viewModel.price, .price) + XCTAssertEqual(viewModel.priceDescription, "Every Month") + } + + func test_thatFactoryMakesProductWithLargeStyle_whenProductTypeIsRenewableSubscription() { + // given + subscriptionPriceViewModelFactoryMock.stubbedMakeResult = .price + subscriptionPriceViewModelFactoryMock.stubbedPeriodResult = "Month" + + let product: StoreProduct = .fake( + localizedPriceString: .price, + productType: .autoRenewableSubscription, + subscriptionPeriod: .init(value: 1, unit: .day) + ) + + // when + let viewModel = sut.make(product, style: .large) + + // then + XCTAssertEqual(viewModel.id, product.productIdentifier) + XCTAssertEqual(viewModel.title, product.localizedTitle) + XCTAssertEqual(viewModel.description, product.localizedDescription) + XCTAssertEqual(viewModel.price, .price) + XCTAssertEqual(viewModel.priceDescription, "Every Month") + } +} + +// MARK: - Constants + +private extension String { + static let price = "10 $" +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Product/SubscriptionDateComponentsFactoryTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Product/SubscriptionDateComponentsFactoryTests.swift new file mode 100644 index 000000000..ccfaef44f --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Product/SubscriptionDateComponentsFactoryTests.swift @@ -0,0 +1,59 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import XCTest + +final class SubscriptionDateComponentsFactoryTests: XCTestCase { + // MARK: Private + + private var sut: SubscriptionDateComponentsFactory! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + sut = SubscriptionDateComponentsFactory() + } + + override func tearDown() { + sut = nil + super.tearDown() + } + + // MARK: - Tests + + func test_thatDateComponentsFactoryCreatesDateCompoments_whenUnitIsDay() { + // when + let components = sut.dateComponents(for: .init(value: 10, unit: .day)) + + // then + XCTAssertEqual(components.day, 10) + } + + func test_thatDateComponentsFactoryCreatesDateCompoments_whenUnitIsWeak() { + // when + let components = sut.dateComponents(for: .init(value: 10, unit: .week)) + + // then + XCTAssertEqual(components.weekOfMonth, 10) + } + + func test_thatDateComponentsFactoryCreatesDateCompoments_whenUnitIsMonth() { + // when + let components = sut.dateComponents(for: .init(value: 10, unit: .month)) + + // then + XCTAssertEqual(components.month, 10) + } + + func test_thatDateComponentsFactoryCreatesDateCompoments_whenUnitIsYear() { + // when + let components = sut.dateComponents(for: .init(value: 2010, unit: .year)) + + // then + XCTAssertEqual(components.year, 2010) + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Products/ProductsPresenterTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Products/ProductsPresenterTests.swift new file mode 100644 index 000000000..5f80725a3 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Products/ProductsPresenterTests.swift @@ -0,0 +1,70 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +import FlareMock +@testable import FlareUI +import FlareUIMock +import XCTest + +final class ProductsPresenterTests: XCTestCase { + // MARK: Properties + + private var iapMock: FlareMock! + private var viewModelMock: WrapperViewModel! + + private var sut: ProductsPresenter! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + iapMock = FlareMock() + sut = ProductsPresenter( + ids: [], + iap: iapMock + ) + viewModelMock = WrapperViewModel( + model: ProductsViewModel( + state: .products([]), + presenter: sut + ) + ) + sut.viewModel = viewModelMock + } + + override func tearDown() { + viewModelMock = nil + iapMock = nil + sut = nil + super.tearDown() + } + + // MARK: Tests + + func test_thatPresenterFetchesProducts_whenViewDidLoad() { + // given + let product: StoreProduct = .fake() + iapMock.stubbedInvokedFetch = [product] + + // when + sut.viewDidLoad() + + // then + wait(self.viewModelMock.model.state == .products([product])) + XCTAssertEqual(iapMock.invokedFetchCount, 1) + } + + func test_thatPresenterThrowsError_whenViewDidLoad() { + // given + iapMock.stubbedFetchError = IAPError.unknown + + // when + sut.viewDidLoad() + + // then + wait(self.viewModelMock.model.state == .error(.unknown)) + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/StoreButton/StoreButtonPresenterTests.swift b/Tests/FlareUITests/UnitTests/Presentation/StoreButton/StoreButtonPresenterTests.swift new file mode 100644 index 000000000..933f66d00 --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/StoreButton/StoreButtonPresenterTests.swift @@ -0,0 +1,41 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import FlareUIMock +import XCTest + +final class StoreButtonPresenterTests: XCTestCase { + // MARK: Properties + + private var iapMock: FlareMock! + + private var sut: StoreButtonPresenter! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + iapMock = FlareMock() + sut = StoreButtonPresenter(iap: iapMock) + } + + override func tearDown() { + iapMock = nil + sut = nil + super.tearDown() + } + + // MARK: Tests + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func test_thatPresenterRestoresTransactions() async throws { + // when + try await sut.restore() + + // then + XCTAssertEqual(iapMock.invokedRestoreCount, 1) + } +} diff --git a/Tests/FlareUITests/UnitTests/Presentation/Subscriptions/SubscriptionsPresenterTests.swift b/Tests/FlareUITests/UnitTests/Presentation/Subscriptions/SubscriptionsPresenterTests.swift new file mode 100644 index 000000000..63f2e4aeb --- /dev/null +++ b/Tests/FlareUITests/UnitTests/Presentation/Subscriptions/SubscriptionsPresenterTests.swift @@ -0,0 +1,132 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import Flare +@testable import FlareUI +import FlareUIMock +import XCTest + +// MARK: - SubscriptionsPresenterTests + +@available(watchOS, unavailable) +final class SubscriptionsPresenterTests: XCTestCase { + // MARK: Properties + + private var sut: SubscriptionsPresenter! + + private var viewModelMock: WrapperViewModel! + private var iapMock: FlareMock! + private var viewModelFactoryMock: SubscriptionsViewModelViewFactoryMock! + + // MARK: XCTestCase + + override func setUp() { + super.setUp() + + iapMock = FlareMock() + viewModelFactoryMock = SubscriptionsViewModelViewFactoryMock() + + sut = SubscriptionsPresenter( + iap: iapMock, + ids: Array.ids, + viewModelFactory: viewModelFactoryMock + ) + + viewModelMock = WrapperViewModel( + model: SubscriptionsViewModel( + state: .loading, + selectedProductID: nil, + presenter: sut + ) + ) + + sut.viewModel = viewModelMock + } + + override func tearDown() { + iapMock = nil + viewModelFactoryMock = nil + + super.tearDown() + } + + // MARK: Tests + + func test_thatPresenterShowsProducts_whenViewDidLoad() throws { + // given + let autoRenewableProduct = StoreProduct.fake(productType: .autoRenewableSubscription) + + iapMock.stubbedInvokedFetch = [ + autoRenewableProduct, + .fake(productType: .consumable), + .fake(productType: .nonConsumable), + .fake(productType: .nonRenewableSubscription), + ] + viewModelFactoryMock.stubbedMake = [.fake(), .fake(), .fake()] + + // when + sut.viewDidLoad() + + // then + wait(self.viewModelMock.model.numberOfProducts == 3) + + XCTAssertEqual(viewModelFactoryMock.invokedMakeParameters?.products, [autoRenewableProduct]) + + let ids = try XCTUnwrap(iapMock.invokedFetchParameters?.productIDs as? [String]) + XCTAssertEqual(ids, Array.ids) + } + + func test_thatPresenterReturnsProduct() { + // given + let autoRenewableProduct = StoreProduct.fake(productType: .autoRenewableSubscription) + + iapMock.stubbedInvokedFetch = [ + autoRenewableProduct, + .fake(productType: .consumable), + .fake(productType: .nonConsumable), + .fake(productType: .nonRenewableSubscription), + ] + viewModelFactoryMock.stubbedMake = [.fake(), .fake(), .fake()] + + // when + sut.viewDidLoad() + + // then + wait(self.sut.product(withID: autoRenewableProduct.productIdentifier) == autoRenewableProduct) + } + + func test_thatPresenterSubscribesToAProduct() async throws { + // given + let autoRenewableProduct = StoreProduct.fake(productType: .autoRenewableSubscription) + let fakeTransaction = StoreTransaction.fake() + + iapMock.stubbedPurchase = fakeTransaction + + iapMock.stubbedInvokedFetch = [ + autoRenewableProduct, + .fake(productType: .consumable), + .fake(productType: .nonConsumable), + .fake(productType: .nonRenewableSubscription), + ] + viewModelFactoryMock.stubbedMake = [.fake(), .fake(), .fake()] + + // when + sut.viewDidLoad() + sut.selectProduct(with: autoRenewableProduct.productIdentifier) + + wait(self.viewModelMock.model.selectedProductID != nil) + + let transaction = try await self.sut.subscribe(optionsHandler: nil) + + // then + XCTAssertEqual(transaction, fakeTransaction) + } +} + +// MARK: - Extensions + +private extension Array where Element == String { + static let ids: [String] = ["subscription"] +} diff --git a/Tests/SnapshotTests/Helpers/SnapshotTestCase.swift b/Tests/SnapshotTests/Helpers/SnapshotTestCase.swift new file mode 100644 index 000000000..0225fa739 --- /dev/null +++ b/Tests/SnapshotTests/Helpers/SnapshotTestCase.swift @@ -0,0 +1,102 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import SnapshotTesting +import SwiftUI +import XCTest + +#if canImport(UIKit) + import UIKit +#elseif canImport(Cocoa) + import Cocoa +#endif + +// MARK: - SnapshotTestCase + +@available(watchOS, unavailable) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +class SnapshotTestCase: XCTestCase { + // MARK: Properties + + private var osName: String { + #if os(iOS) + return "iOS" + #elseif os(macOS) + return "macOS" + #elseif os(tvOS) + return "tvOS" + #else + return "unknown" + #endif + } + + // MARK: Tests + + func assertSnapshots( + of view: some View, + size: CGSize, + userInterfaceStyle: UserInterfaceStyle = .light, + file: StaticString = #file, + testName: String = #function, + line: UInt = #line + ) { + #if os(iOS) || os(tvOS) + SnapshotTesting.assertSnapshots( + of: view, + as: [ + .image( + layout: .fixed(width: size.width, height: size.height), + traits: UITraitCollection(userInterfaceStyle: userInterfaceStyle.userInterfaceStyle) + ), + ], + file: file, + testName: testName + osName, + line: line + ) + #elseif os(macOS) + SnapshotTesting.assertSnapshots( + of: ThemableView(rootView: view, appearance: userInterfaceStyle.appearance), + as: [.image(precision: 1.0, size: size)], + file: file, + testName: testName + osName, + line: line + ) + #endif + } + + enum UserInterfaceStyle { + case light, dark + + #if os(iOS) || os(tvOS) + var userInterfaceStyle: UIUserInterfaceStyle { + switch self { + case .light: + return .light + case .dark: + return .dark + } + } + + #elseif os(macOS) + var appearance: NSAppearance? { + switch self { + case .light: + return .init(named: .vibrantLight) + case .dark: + return .init(named: .darkAqua) + } + } + #endif + + var colorScheme: ColorScheme { + switch self { + case .light: + return .light + case .dark: + return .dark + } + } + } +} diff --git a/Tests/SnapshotTests/Helpers/ThemableView.swift b/Tests/SnapshotTests/Helpers/ThemableView.swift new file mode 100644 index 000000000..14c374083 --- /dev/null +++ b/Tests/SnapshotTests/Helpers/ThemableView.swift @@ -0,0 +1,24 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +#if os(macOS) + import SwiftUI + + final class ThemableView: NSHostingView { + required init(rootView: Content, appearance: NSAppearance?) { + super.init(rootView: rootView) + self.appearance = appearance + } + + @available(*, unavailable) + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @MainActor required init(rootView _: Content) { + fatalError("init(rootView:) has not been implemented") + } + } +#endif diff --git a/Tests/SnapshotTests/ProductInfoViewSnapshotTests.swift b/Tests/SnapshotTests/ProductInfoViewSnapshotTests.swift new file mode 100644 index 000000000..50d3a4c99 --- /dev/null +++ b/Tests/SnapshotTests/ProductInfoViewSnapshotTests.swift @@ -0,0 +1,81 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import SwiftUI +import XCTest + +// MARK: - ProductInfoViewSnapshotTests + +@available(watchOS, unavailable) +final class ProductInfoViewSnapshotTests: SnapshotTestCase { + func test_productInfoView_compactStyle_whenIconIsNil() { + assertSnapshots( + of: ProductInfoView( + viewModel: .viewModel, + icon: nil, + style: .compact, + action: {} + ), + size: .size + ) + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 12.0, *) + func test_productInfoView_compactStyle_whenIconIsNotNil() { + assertSnapshots( + of: ProductInfoView( + viewModel: .viewModel, + icon: .init(content: Image(systemName: "crown")), + style: .compact, + action: {} + ), + size: .size + ) + } + + #if os(iOS) + func test_productInfoView_largeStyle_whenIconIsNil() { + assertSnapshots( + of: ProductInfoView( + viewModel: .viewModel, + icon: nil, + style: .large, + action: {} + ), + size: .largeSize + ) + } + + func test_productInfoView_largeStyle_whenIconIsNotNil() { + assertSnapshots( + of: ProductInfoView( + viewModel: .viewModel, + icon: .init(content: Image(systemName: "crown")), + style: .large, + action: {} + ), + size: .largeSize + ) + } + #endif +} + +// MARK: - Constants + +private extension CGSize { + static let size = value(default: CGSize(width: 375.0, height: 76.0), tvOS: CGSize(width: 1920, height: 1080)) + static let largeSize = value(default: CGSize(width: 375.0, height: 400.0), tvOS: CGSize(width: 1920, height: 1080)) +} + +private extension ProductInfoView.ViewModel { + static let viewModel = ProductInfoView.ViewModel( + id: UUID().uuidString, + title: "My App Lifetime", + description: "Lifetime access to additional content", + price: "$19.99", + priceDescription: nil + ) +} diff --git a/Tests/SnapshotTests/ProductPlaceholderViewSnapshotTests.swift b/Tests/SnapshotTests/ProductPlaceholderViewSnapshotTests.swift new file mode 100644 index 000000000..3e93bf51c --- /dev/null +++ b/Tests/SnapshotTests/ProductPlaceholderViewSnapshotTests.swift @@ -0,0 +1,49 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import Foundation + +// MARK: - ProductPlaceholderViewSnapshotTests + +@available(watchOS, unavailable) +final class ProductPlaceholderViewSnapshotTests: SnapshotTestCase { + func test_productPlaceholderView_compactStyle_whenIconIsHidden() { + assertSnapshots( + of: ProductPlaceholderView(isIconHidden: true, style: .compact), + size: .size + ) + } + + func test_productPlaceholderView_compactStyle_whenIconIsVisible() { + assertSnapshots( + of: ProductPlaceholderView(isIconHidden: false, style: .compact), + size: .size + ) + } + + #if os(iOS) + func test_productPlaceholderView_largeStyle_whenIconIsHidden() { + assertSnapshots( + of: ProductPlaceholderView(isIconHidden: true, style: .large), + size: .largeSize + ) + } + + func test_productPlaceholderView_largeStyle_whenIconIsVisible() { + assertSnapshots( + of: ProductPlaceholderView(isIconHidden: false, style: .large), + size: .largeSize + ) + } + #endif +} + +// MARK: - Constants + +private extension CGSize { + static let size = value(default: CGSize(width: 375.0, height: 76.0), tvOS: CGSize(width: 1920, height: 1080)) + static let largeSize = value(default: CGSize(width: 375.0, height: 400.0), tvOS: CGSize(width: 1920, height: 1080)) +} diff --git a/Tests/SnapshotTests/ProductViewSnapshotTests.swift b/Tests/SnapshotTests/ProductViewSnapshotTests.swift new file mode 100644 index 000000000..3500ab8f2 --- /dev/null +++ b/Tests/SnapshotTests/ProductViewSnapshotTests.swift @@ -0,0 +1,82 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import FlareMock +@testable import FlareUI +import FlareUIMock +import SwiftUI +import XCTest + +// MARK: - ProductViewSnapshotTests + +@available(watchOS, unavailable) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +final class ProductViewSnapshotTests: SnapshotTestCase { + func test_productView_loading() { + assertSnapshots( + of: ProductWrapperView( + viewModel: .init(state: .loading, presenter: ProductPresenterMock()) + ), + size: .size + ) + } + + func test_productView_product() { + assertSnapshots( + of: ProductWrapperView( + viewModel: .init(state: .product(.fake()), presenter: ProductPresenterMock()) + ), + size: .size + ) + } + + func test_productView_error() { + assertSnapshots( + of: ProductWrapperView( + viewModel: .init(state: .error(.unknown), presenter: ProductPresenterMock()) + ), + size: .size + ) + } + + func test_productView_customStyle_product() { + assertSnapshots( + of: ProductWrapperView( + viewModel: .init(state: .product(.fake()), presenter: ProductPresenterMock()) + ).productViewStyle(CustomProductStyle()), + size: .size + ) + } +} + +// MARK: ProductViewSnapshotTests.CustomProductStyle + +@available(watchOS, unavailable) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +private extension ProductViewSnapshotTests { + struct CustomProductStyle: IProductStyle { + @ViewBuilder + func makeBody(configuration: ProductStyleConfiguration) -> some View { + switch configuration.state { + case .loading: + Text("Loading") + case let .product(item): + VStack { + item.localizedPriceString.map { Text($0.debugDescription) } + Text(item.localizedTitle) + Text(item.localizedDescription) + } + case let .error(error): + Text(error.localizedDescription) + } + } + } +} + +// MARK: - Constants + +private extension CGSize { + static let size = value(default: CGSize(width: 375.0, height: 812.0), tvOS: CGSize(width: 1920, height: 1080)) +} diff --git a/Tests/SnapshotTests/ProductsViewSnapshotTests.swift b/Tests/SnapshotTests/ProductsViewSnapshotTests.swift new file mode 100644 index 000000000..e50509bbd --- /dev/null +++ b/Tests/SnapshotTests/ProductsViewSnapshotTests.swift @@ -0,0 +1,86 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +import FlareMock +@testable import FlareUI +import FlareUIMock +import Foundation + +// MARK: - ProductsViewSnapshotTests + +@available(watchOS, unavailable) +@available(iOS 13.0, macOS 10.15, tvOS 13.0, *) +final class ProductsViewSnapshotTests: SnapshotTestCase { + func test_productsView_error() { + assertSnapshots( + of: ProductsWrapperView( + viewModel: ProductsViewModel( + state: .error(.storeProductNotAvailable), + presenter: ProductsPresenterMock() + ) + ), + size: .size + ) + } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func test_productsView_products_withRestoreButtons() { + let iapMock = FlareMock() + iapMock.stubbedInvokedFetch = [.fake(), .fake(), .fake()] + + assertSnapshots( + of: ProductsWrapperView( + viewModel: ProductsViewModel( + state: .products(iapMock.stubbedInvokedFetch), + presenter: ProductsPresenterMock() + ) + ) + .environment(\.productViewAssembly, ProductViewAssembly(iap: iapMock)) + .environment( + \.storeButtonsAssembly, + StoreButtonsAssembly( + storeButtonAssembly: StoreButtonAssembly(iap: FlareMock()), + policiesButtonAssembly: PoliciesButtonAssembly() + ) + ) + .storeButton(.visible, types: .restore, .restore), + size: .size + ) + } + + @available(iOS 15.0, tvOS 15.0, watchOS 8.0, macOS 12.0, *) + func test_productsView_products() { + let iapMock = FlareMock() + iapMock.stubbedInvokedFetch = [.fake(), .fake(), .fake()] + + assertSnapshots( + of: ProductsWrapperView( + viewModel: ProductsViewModel( + state: .products(iapMock.stubbedInvokedFetch), + presenter: ProductsPresenterMock() + ) + ) + .environment(\.productViewAssembly, ProductViewAssembly(iap: iapMock)) + .environment( + \.storeButtonsAssembly, + StoreButtonsAssembly( + storeButtonAssembly: StoreButtonAssembly(iap: FlareMock()), + policiesButtonAssembly: PoliciesButtonAssembly() + ) + ), + size: .size + ) + } +} + +// MARK: - Constants + +private extension CGSize { + static let size = value( + default: CGSize(width: 375.0, height: 812.0), + tvOS: CGSize(width: 1920, height: 1080), + macOS: CGSize(width: 1920, height: 1080) + ) +} diff --git a/Tests/SnapshotTests/SubscriptionsViewSnapshotTests.swift b/Tests/SnapshotTests/SubscriptionsViewSnapshotTests.swift new file mode 100644 index 000000000..512f07f3d --- /dev/null +++ b/Tests/SnapshotTests/SubscriptionsViewSnapshotTests.swift @@ -0,0 +1,70 @@ +// +// Flare +// Copyright © 2024 Space Code. All rights reserved. +// + +@testable import FlareUI +import FlareUIMock +import SwiftUI +import XCTest + +// MARK: - SubscriptionsViewSnapshotTests + +@available(watchOS, unavailable) +final class SubscriptionsViewSnapshotTests: SnapshotTestCase { + // MARK: Properties + + // MARK: Tests + + func test_subscriptionsView_defaultStyle() { + assertSnapshots( + of: makeView(), + size: .size + ) + } + + func test_subscriptionsView_customStyle() { + assertSnapshots( + of: makeView() + .subscriptionMarketingContent(view: { Text("Header View") }) + #if os(iOS) + .subscriptionBackground(Color.gray) + .subscriptionHeaderContentBackground(Color.blue) + .subscriptionButtonLabel(.multiline) + #endif + .storeButton(.visible, types: .policies) + .tintColor(.green) + .subscriptionControlStyle(.button), + + size: .size + ) + } + + // MARK: Private + + private func makeView() -> SubscriptionsWrapperView { + SubscriptionsWrapperView( + viewModel: SubscriptionsViewModel( + state: .products( + [ + .init(id: "1", title: "Subscription", price: "5,99 $", description: "Description", isActive: true), + .init(id: "2", title: "Subscription", price: "5,99 $", description: "Description", isActive: false), + .init(id: "3", title: "Subscription", price: "5,99 $", description: "Description", isActive: false), + ] + ), + selectedProductID: "1", + presenter: SubscriptionsPresenterMock() + ) + ) + } +} + +// MARK: - Constants + +private extension CGSize { + static let size = value( + default: CGSize(width: 375.0, height: 812.0), + tvOS: CGSize(width: 1920, height: 1080), + macOS: CGSize(width: 1920, height: 1080) + ) +} diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..47ae24db172603741626df291230dc141f767740 GIT binary patch literal 21459 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 sjV4Z%Og)mdKI;Vst092P|M*si- literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-macOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNil-macOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..8d764c4de213a4fd9be5bb7fba81593b514ec6d0 GIT binary patch literal 24663 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�)IhecK4_pZB4wpa0e`MeYg?uv(pFeM%F1+|l7 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 O(ALzul7GqO;r|0|Hql1_ literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..73094338463d20ebe7610cd8f5adc77f5f2b1b07 GIT binary patch literal 23833 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 W*VZ;Pk_W);O-LgP!_pJyuKfp?OUPgV literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-macOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-macOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6c02a5802442066806ffae259dcbc78b0f29902e GIT binary patch literal 25757 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_compactStyle_whenIconIsNotNil-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..9df73135c02c19e4f4b36b1adf0fe7f97045efb2 GIT binary patch literal 52972 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%0DktDAw)y>~yQGJ<^e)T!x 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNil-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNil-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..d68f4da9c21e8bc4e8d0d53af5649f95d51c115a GIT binary patch literal 44994 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 KzG$cK#s31RW-&_u literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNotNil-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductInfoViewSnapshotTests/test_productInfoView_largeStyle_whenIconIsNotNil-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..e851f561df4938129e1560b2118d998e24373d7c GIT binary patch literal 47452 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 !H z4^P51u5B*?&6<;m12Jz4QsY!bz>qmO4H|JUnHzTkUC~%r~VWt`upB zXz49X2ekUbP<{*_JMe-1o2G-7X<9LX!J1Xa#l`~$Uq%Nyrl<`YulW`Yn^1)MU?@Vd zfHvV_RfOsMxvt`tpx$?7nmyYIA2Nni^fuIfj{4q3Zh6DSj|9rbmdj>w303H`UiGV< z#9+O|=di!#b~?PV%w3N(5-O_b{WYtpj^Pq#$ozHt<93&-C%#1{+>0eX$(|F{UacLM zq)Jc&e1TzdJz5)hayql`mm1C*X3TeN!^7R%?Pp%LZ?3{? zUt#|bJtF(E#+S~U$C<^M%>(e1VfajliSnswfL+ z4;$LMS$i8;;4Cnnkh3A0Vu4+TwR@Wmuk-~i%{(Z}lThqLV#0)SwU1b~#IIEWvK`{E zavog~u(tUCX@(%Ply8!pZ&DRMkaP3W+z(oAcV0$RMwF$&1}tGsmZG{?$Q5klO&GzU<;GncFn{+eg0CU~?AQxgf1az# zLdKTmFm!UiLArq1g=N}1A`MM5N?5uF*z3cSB|lJz37X+r7#VD-uk^A$;+Y$Pia^;5 zZvY=~PkoqbA47UKM6%gx$-nYw=w6KItSfZ5mhiO#&<3u;`3|(`650|*U&m1i9v02v zvzAyVAewTv*a|$h;tQ@kcx=Yf!@^A8jFO+ZN&Wu(=P7lJ@~X@}yZAT#Voh5Nydy@& zx>aDlGWEQo_q41F z$v@NUmZ$Eu%Y+r;gHS|O7tknLGs&LitE)wcKZ^1fjt-CI!}q^yoVDwrI=|-Ww**i7 zPwim>HHd)39b!xk7k89!dSipGX)ZmYn>|gKv>f*(w2w6z-zL#xSrDplVy14oKk@HR z{h5yo6&1&9bS~tdKwY}&5#Qroq)Pg31uNRH8RZ=e8>+s#AQ)y6qa8{P;=-G~$9SKf zds8>@xGMGZBbyr1q`z!%d{5K8FQX~=j)Dz*$TgxVo{WMYCFXrtUBgR7&8?*(FsEuk zrz6TxCARHA*+~(L!MFY2Z$G~uBCW-8R|=~dH$Wt1v@|Eu=%+BEM|@IhbwrojRJ)GD zVqaxr#{wj&S$n_e2C1825;h9#bR03;{?+aakP&}zbh;POj^5Fy`a z;}!H3DCe)=ccWk_8oWoA<)$Q4^&f{}Nz)F?4A-{)LaxYIfw-hsbcT_yszu}Tf)Xe&>TE3PX zo`Fd{@es3fRoIQ}6gqyXalOrxJpiprdwb_JS6XR`>rX|3xbDeNF?EiftBv`Jm3l7E zcxB4BA*L0WjRXJsLwXXtY@lsBI5x4&0cM z3IRcdje9IXayRQqjCAj&6DxK!wZn1p0RGxQHRU_12PGO*8l(j literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-macOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-macOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d4325011d3b420d47c16d2c6e79cbdb9d3b1f6 GIT binary patch literal 6273 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 TmYCV2FL_$GbT!_q+x+!Ef07_h literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsHidden-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6b85ea1cf9224b181c9718ab005a54705b0fb66d GIT binary patch literal 37695 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 PLlCXgx~Fp0ZEyS!SsDyy literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-macOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-macOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c13dfa9bc72777bb867554a6f9fa877aa66b412 GIT binary patch literal 6631 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 J;GXHf{{x|i6;uEK literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_compactStyle_whenIconIsVisible-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..f2d9c1dd018db3685fe877de34d08c3edfc6a858 GIT binary patch literal 39310 zcmeI43se(l7RM(Ei2jZl<_iVYq?P!lRh&<60aNK`1bVCt%Xf*=(n(LNNc zQWa4_6K#>&jg&1S2oj=gkBGbqyMR?7)-9M#c)wrC&Ww*z&J5K(r|0bH-f+SUIWxK6 zcfbGd-ur!rZw~wUcuh8$VS*sYWN(4TS_GLSK@cMsCIkKCam~#Y=z$Ws*6S@KtHM%) z{_$aGfcI9R5V1$knFyUS6`^65pg#y@213Wr5o8tRwddz+DYj3qp(4m9+YqCt*K9X>!Uh-zcX(yLysDPJ4E48seAf}BnNGy0+B zHV@zR=n*xDy{MMxFWV|E$mF;tq1AbiBz!+rf;ithf+EZrP4qpE&`EKPXo@I}$> z*2NaoD67yt%Ss|``vqCIWjwt#UxHAmMocpfoPZNR2|xi*02BZPKmkwy6aWQ40Z;%G z00lq+PyiGF1wa8%02BZPKmkwy6aWQ40Z;%G00lq+PyiGF1wa8%02BZPKmkwy6aWQ4 z0Z;%G00lq+PyiGF1wa8%02BZPKmkxdKmlh#N8LLKhDQkuE{DeLFt7)1Cy;K=l&&z+ z+s5mQd*1zaB_l*#%o;dGx|cH|Q9`XZet)hil+Q?HrE{wq=8_HopR(D4=Ob~Y*weV` z%mWjCX8=XllgTRKeVZ&pi58om)c5_e!8vk&E%o>N7j??HuW+0LmVXM^d~87 zTW&-tKSoB{eF{Bd&4|mW2D2}bnUHQ`RoPKZg6Y{cGSsD>3?KI6wiBdXk|PTeXJztm z`yK1tN%zLOc=9O>qixQ*J;xPBq(lhU^3#-zh(qP{q=Yp4dZPvK1?9ZBgrBMX&DFfNW4 z6K8@eX8#W-&T3i%kK1vK6{3E?`l97vFQI3vrr|@C+SQ^wnHSR+=FccNn7eOOF?{uC z*1Zp~%lP|mVh3SS~&NV9OIZ7t$lJNk_~qoSN2 zlO6qa>Zg%8@3!j#`(tC45lXLmEO6Ba0}g-baZ(!Tlzje`+POp#g1-R|IYFhe;kAv8 z_X zd8YS3y=y6h?cz*pxcOsI(Bg{nkBf3*8d@592~{dE9mA&Jl`S?Gr>?==@xk%{8DTI9 z#G}(*yM(sYvHQgBTSMh;0flvy1-{yn8!Sa7|Al?GEl+v%y^1)0Ms@eiA;PvjHo=0c zxYl%w*7h|_IZ>0g_g{2M@)Szz6qSoL}p!Xx#I{!Z@_b19OdLFgTtt21U{F zpq4Rm-SOwvb46(>(zf8%Af9&l73*$p_Hx<_`!STDb!jlVu{u zLsvfEk&LRSYNnz=8~W(Mj(WF!buTOoD|_=$fC`{OD*!Yg{g+Vv4}(Mu4<6IE8so*+ zwy%0KFQ0a5norANWbGj@L$-}A88q%Yh$8@&@>TQjAg7C-`opNpOb0YjFWj!)_z7V= z#3EPnpT^X7$*+G$oDA^W%cedlM^0&dH*v+vO^mIRH)~1D45M)gr=B=aVmVPN!sGwF z);BH=?{f8L)QW>^Rk>3LQw8Ru@j3GfVIIP7KW+1*dYoS;yB1Orm&YobkzYHTDBs~- z`~r=k&ugA9CUu5=lsDXYY~f07^8O&=Y)qk6;*Dp=8n@-FvS8<1(<ohdGA^ zhmbwYn-lGq?@4qQQ0oKUDs#!3}yo@m=wi;C=MnwIZL6womb&n9U>rNawW&0!Cm zlHx5K?%MfIeye7&-XlOtEZ>!5E7T*bd(N`IgtUo5eoPG??wZq`MLf*Xbi91BK$=XD zum@qK{4gVfzw%CLOs6jMWgM7sxw_{P>j=->pQx-uMQx6Q_@HNPW6T=v9aSC+Z(*)u zQ{PAT&xeI`+h-7_akCOMG(4YjlzgggDord~mh?8}j_S%J;_{QP7X5mx)Uij78hYnA zG<~1LdnpH=wHJn$s?y2|KPLA1Zd~oxH5oVBFNPeKhO_Y&_WgWc8F9a6{^$*o(#z=l zShAKL`=PVgp4&f<+j=)Io}FN&AR*Ekx3&g}b3pQU7246PSM-&O!@ubYTsr(R4qUY^ zLnCAhc#b5|O)4HgR(K0BA>G>Po7Xw_mI+~%$5}Xb-i7m_u!^j-szHfNbv_d_nK*>~ z3)QV>=jX_PKN9gMdL3i8^*~-1&K2*a94Jh=gi<+jIGajr=LkA^P5P9{vT z`GjBvM*V}nsC^TLndcK}l#fkkh_%4fS*z9MXAe4SW^TYHs`9UmUBwk{Vssiq8j_>U z>IVZyh5e@*w~v_xPmt2G52}PV+KMKdqB?AnaBqoaTlr-XJK%)0hjWd1I>9uncjvc9 zc(sq2S_OYHE3ltLWlvL8^_RAvE`4MB>r*SzYuEyVd?WEJ!icHkB=2tu8+|A9MC10k zl9c?!ID9HUVJ9R9qRTH7_5?YgBjQz6XLQb~<($(*`PPl88_CjN+PAtr_gB-Y3Hq$Q zbbL=s`*GB>=pu~Ha!P;mKNKv*9sUPqo5;E{a#uMs9kIB@x6l=9E$4AEQ*7R*S#?|A zN~q_^-5xxiLd`~Wy_xKVw7aZDn+y8LJ&0a7Z^rx|N@fp63S1(6D)~ci<&q80`(^?| zc%FW{t0sII`oJ`U!H(_4eD;*I*86Y%)zZ(5=GSD%4_oW{kygHPh?O>7;1*Z>&!acD zD3!&Ak%3A+BZSY;pOelq5GOB*DuE++7>LBE}PhHn}=nw=cJ~^QAH#Pi>aX3CWJd1 z@@hm0M;AO(&Tm<$QZ6ajlu&D;_8VairO6|kh;i|nB9 znhEE;Xht`s%GQ4qq!(fGaIxT}e*17|*49w9a3U*w34b-dEcwJqRU7l0NqmIH@LDw8 z`Hkdf$3?z-=S=s{EXDM{4-zLHltbw$yr{#L8#y8J+=z1GdIw7l3N-m%7`Psn@)#Nn z9>$Y7Na7iW0NvB14w3*#jGNPgBtQ}%iE$nPNsMy{LynQN*O8w{-TtYqg3Hz6l#}}{>-tDl8K@W8ih(dj8ajQ*$>{KKONu) z+^u+5zNYlg8n2-d}-t z&>-{&?GOGjk-tO74nKdN`ko2i&$(~i{8gM%KX2fOLF1)UFY08?<#nMR@!v~=U*F1E zI&gx|S>gSV$zgK7c1pbdL*Tw0QsLkl*A$czS_PwHIDIfSfTl`D&ZsS#IWv7=unW6- z`go?i3VK&IinsHVDnOwWpY3WNRVu%Ymg2L{)$YQq1`V%ep8wPYP}&uoRezH9*|b&D z?V5y!%r@9i2V)Y7{?15!#wNwVE-ZHXwwX(axeycxx-bV3G9Y9?lwk$|gbXuKAj*J{ z0U^T-0tgvqpg@!XAp=5&83YhA%s_!C140Ib3^NEIWSD^hQ3iwz2pMJ&K*%ry1)>ZH z84xneAb^lz1`0$O5HcWSm_Yy`!weLNG9Y9?$S{KdLWUVA5M@BffRJGZf&Uva1a1=y z4GqOnWl=RXHLJY6iEN=z^lotQ7-h1zepy4x{064XQCT!9n%GpG%rD|si$=v0oBFpw z+t?;GOhQL^wHdmGC8bO@yYHPua*MLpxe>Za)w(2kRWZN$5VSe3BtON6P@m^$rG(DW zEc7PG`1#TaS)abDwV|^0pa|<QCxx@XUr7ll1XYs3!P5n__qF_CDM7~MTh{r98#Kq(I;>ND0m{OQ>7P`6zz=7zoJAvKdyuw^mCRV^}$HayvN7TO?2&@>4ph`Db7vLq)b%9L+FKCbM759E$1l0bHmEw5 zoY<(`!-6SsAHYA7oR|AwK3lXs9xHxDTBTOxw&`lUh^4O|2F^7GBZG0Bd^aJ0g zMA`928^KZfEcRZ@+}i{zQQ90A1;PC+ZTztDhu+Cc-_QRrhkbu-ycOT)b^T4;yqG;< z%&uj#+|bqZ2H4>XyheYHi_FIzX{%U&dcSTN-LMe*dJg;k1LNy%KB0O%)at)Bms|L3 z|0|z)p8q6Ad|-|BlR51B4~(aHT$f!szM^CE|J31W!WR#XdBeuM-sWpS@u`i>iKziN zFnq)u`<1JLl6EBvy&GlCCvs)_swwk14RaQ}dbMYUrNL_LVxq+?`Cz_#|Nd=o|7mS} z-5X0`j@HPxjQR_6s$EUJY-Z3;10}DQEBQxK!tU!U0D|L8pK2-)Nr_30Ths`1iefIh ztxZ})Pkn~fz6~0z28DX$$~g{{bKEh;4OnTgYE|8aGSENr`VAqt?ZEh>a^o$4_SNH! z!9H-NsestbiQ6;8Jj$@PbWxUGqT7M^gXuTAmf0D`_1Xp8T{t{kU2h>2H^XE<1+?B< zuJz8Ksic^JzXMOSSEb+y(t`dv_h7a)scrt)X}aAJs2*cb53$c{H>hWb$QcvXk5axJ zDmhBDwzL4jrL=yvuq7M#RU#C#yQ_V5L3~;4FYEquB4p&%t0@p_9KlF2Qd8r#omPG` z@D=cN4S-w_SKK)~88g*$u%0?q#_c%=wyBp?07({4Ic|a+<)d^(N8KcJO&osKMs`{) zkdZ_&|IO_4J6%n6AL@I|+7I6k!bNAI%*%V$`K@T#dmiU3ina+d`7Q6Zx`9~il4->c^eLve50^C-K+&gx>4o)=wE+V>0woU> zWaD+Dn!04a29pp-BIPf^H=xWHP#KVh^p_J0)(Z5k^{OcYAJngvP5gX7QBJx^xz0-X z$ZI8iq*{7X@1j7w6%P%}0;uB8cIz2e!0O3nvO;{^htyIMQ11;91q z2?fljPjfsvJk5ihw@D@nzAyBbcVTx6^Fej|LPN_SkV;V5UONSg<0zDUilCxEY#l(% z4j|SG(w1Ivhq`($T3g%MJ!iuG3c2yV0trs@ztltu$cYUo^L4zau)PyYN(1TKblL_S z?>F1Y-w5?1W)LO7Ysgx#+#`ia0o0MKa9mCEWo|p#v#rGoB0X#iA&mLurH{Ln;?dRb zku&vs@r)HI;h!s^hX756V*5|3cq!?X$LoFRSnjJpCo4~h3%L4GSIm!=SsWb+ua}zu zIb~7lCxa09toPuv{%2mHayW1?YJtF&vZfV;q)~aVrS4v5@l=zX70jCXm8?Y{LrU74 z6T24y0&puXD|TFpAmFJkFA<&Vf{3?q<9Hm0Um$>#4KRiuo$|hye z+um&-6B10xd=*)Tg48)7I35r9_6#2eeFfv4{EP3@d0dz4_+NG@sC#98p<@o1NF;(U z6^(S1`2z2DcBUL)y}DZ!c&yJ{;Q>Ky%m-z_AOGpz`Dp2V4%cFBlh8DRL_cLS<7E6bHUQ;irD{34| z0^?>y-O}3@`;|V1YYaP04u&$J{>-YET-U z2HMr%(6x7V1QP*}i^?=z)IX^P1jzG1HE)DEuAg}YyMm^%b|hB`fMxFdwIV6-EJv0%I-m-@=`A$f9RFgME#-%78t>3x#r z@#sq9p{=>*P#7$910MZD@cYl8AS(D>m9<2xYrq#}L>b@sf>o~SiH}UXM&OiN^%=;0 zo_QCv7CIGe4BVaOuFBe{sjXctR13fuoeWqnao=gv`!}=0>yoUF{SK8c`7vb=VQ+E^ zR5{L;78U)7P!>(p8!aC{M%<=9T8bqysz?|{u{R2@kS=G325l8%I*+F_K7C3h%crfc2Ss~Yw(9+^ zyhK)I80Wd_G2vAke59j1{h$r0V?tZQ-kgU%;*Qax*3u?fl>S}w{YnbiM>$b`ExV=P z^nHNgi~v}b?M*O*I+qoI>s~zEOCU%4?Z9}n>t6bTeS{NjhrT+#q^%0f`cs%yDkrV` zE_|?uyZ;k&YU+d~tLx&eTxI;*>9!e~m{8})z91<4Ghd=3dqwLn^gcHYD!-hA@|L>2 zmN)AQn)QA+&W03p)*=i>#i5y=fg==g zw}io~1^IfQOwSV|C}&}QhVHq0kF!09dDlFa7<%QU>aT}kqXR%E!-78;!nU1YR$jlI zf;*^0d$*rZ!`dGO-3nS0gKqZNz1M>Zxe5gM;>(@gn)}*CZ-sYjNpR{z8&y4NE!H2` zgrN<@RdiLm^>!sZR(jZa3~$$-egf(hMrW)nWlwLLZSodakJ9tkk;!EXn=qjqnl^`4 z8beu=p$DE-fK~>>l&*8~k4jF%_ZhG}Jq{i&?Y_9QpG$OUKc}e8euH7A!@wT}aURc>@UcUNlosvB?LenC+#4wQ>O`iHT+oQ!`zgq-xYrHY+H_TqJS$LnWs_rZ;l zfbWsfzc658r7-HR+BoSfKN#7awEMoI#Nd7~NWcH=t>D^JPr(LtV{<((PK0CorRhGd zHOw3jec>ptaxsYQU=t!b$T5GfosW)o>lCnacN)n z${|;|3I>LNhEa=KCs>cYP7}PoOJ+W|ZaYMxz~lQAjLNNiGzv1t5Is3{gTWkG*wO16 ztVJ)Mu0;!^4FT{NKGkq?uIpYFX?sBb{iD;}7fmkLE(%>xhyQ7lOLEZJHfmp@+~5{q zTKLL?eelvYQNxLkNf#3%BQ9}Eu8$gUUA)=*CzGaXtNf?rzu+P8HW<-e^V$!?j{zpp zZFZ-$wH^H$D|fLBOj>EBy~mt_&W>lx=MV%PohFWDQ|o!C-(FDNJ-m3)1+zWvRMn;{ zEK*^WK^ym*g+aYKfpyuQJdSc+(y*0bO@q9t+OUN!c^55RjiJ5qI zvpM3yDQqA=M|phlt-@b2G@on3vlHeL0A5iXDQ`K1aLtBuX@ZzZfU;j>ma-L9=&!t_PY^?m7u-0s&L~2d`Z1iH|eo6C9Q5(!0=5q|F zn_!ZMf$EmrakV}E)|i~Vpu+HkD3NRk7v_H1FP0nfJ8+!vRh!#j$#eeP()PGeRGW5# zuc^nOHMh_TA8T8L$j-rTQ0Ncz_>YG}&tNelqlN2kRPzp*lrd|!o;0|u>CjGaY{x~8 zO;#-#2!a$0MH>d%*ltI-3LYq`U~=7Y5h`La?`7=KoVd2p2k5*~wNut%344buA6~YoQ+#BDw=0G3PM>DFbIy=r;RIZK z^TsV7y(m{18WsRylm37VH-94~N1~z9y23K9hS6erovs%Ox4aLuENXBh!YxNsD0nu8 zCgc8%aS;&RElO6$MmMsH%&XQ_lrCKX%K&aW=q>%m+gun+lR;rB=<8EfPgS8^H#YoN zdAuS3mF3v~*$Ve29W7M5Vf$XhYtXM2VXOa5ML1vTrkn(Pm9+H3U6?H@vnl~K@Ei0W zi?CZPJm#`QFyO&0yRO$-nD}{ZQsY@`2y)JP!_4gHm-0Yi(EU1=ryswWGfdCdpGw#a z;}rnFD@(5#fc1H_(O0tZ~VheqNsh25sk*;D(k5wtAj{eq@8y@mrM=^1>3X>{LjiWG0Pjk1lHczR>&UG;$`}q z?IsV(ZXL2LduwIzZ&<1Y@U%88SJHXMl3)F1YUtXD*k~oe)L|cEjlGXa)~1N~v<2S_RiznGAS1%qX>iRn7$y zVpPd{&{<5v^Iov_vx$YZQ6U?o!M7)157w#N4@y7R`bigl>^Z7(ccFnD_Mqri`_R%` zh4ET1)#?Mzihl~IhtdKiGc7^~LbpAGNLQVtY>*D4 zM^a%T3N(P(ewN>R(I9_yUN=ZFdR=Gjmi?uget2?gRX)eRwvGD-`|C0){P0Sp-PABM zpszB%9mZFU)~Ek4H8rooGD(+H=ogyxWlU3J}(86|Mk!o{pw2jjJ;?pq6(=9NZv0 zf+b&r7@3$Xsau*aYaNxFe+Bpe*XcoP8f3oIompV{)6hRO{hAAj-S2OE$nLpLDL`}s zhAh$ghcEnKpP?xkixTfT^XTHD<3(vs3FEhnMTK`G+DT7SK5Fwg4WStbu}6nGf<~zh=x5uT+gd z=RWJXGY7D6`L@;AO5M;|&vh{Ija_@@bT$KEkAL$*1~3A7-Ci7JtGK15+4H8Stu|SJ z2kx?VRpP)e@)fL_r8-SDw(oT1h%WAkjEx*ic&J~eVeh<@RQT)B!nJZqH~cuUNnv^` z*-hmc+xz1?N>BIo_tv=tc~Rsk+aVB84xJxUg1I}kcJPDktu!-2pcP(=g462mj;J~M zhTD8bEVs=J;B2)vTIYbSPb?7pY|j*9sbSk-jPcIg^hAg|LD%*6z| z&S(dkt~MD@*z6v@n{{P64<$l*=*T;HP#*MUvP0VRE$Y-HWAb#d20|!< zZ6Ob^7PKfT)=s*F6X-!Yc6(@9_|NPhSpshm+7W`$iSqy%-KBIsl)j*7d=(a9Xk?`4 zKz7Z=Mgx1Rd92@c8YClsdqI`gr4LNCBT0UM7>3DRq{FG)-|OR-T{s@4YF%(>g9Lse z57G(Heixf4zsH@nkP~N?K_axQZgR-4uJ}BUl07Kr_+Sx8o(`^|!rrRlrAf!JTBxcv zee>^ZTR^fU3PMJg#uF9u>?z)n}a^Mdj90=h|%nTf!foo z0~c8aF|d{@p)W7gX$oXLmoKr>^w7dvKjKd$p1HHk>DHZNt()et0;7%idLA7L?L7;U zFY`x#ZkB_8m0*9T=15iXhru{%?C_JUM7&mdm3{Bu6)yN1gsTTV59FN5KnwpoIWV$$r%>a{*+ZK% zbO@X;{eI33{2_TK4Cy~WKi5|fdMhk@69b>f=4CBAKR8@hLgV4$g=Wd zC#8YlwVrQXXS8f#XazSxHRI@!HkiCpix-tmgzvH!a@1rbTd?Y5w zxvD`zhMU*}npj*~E^h+16gC~R)zw5-efDCwAyOK_1Xe(WhpHevL44;U)c{X15IsRu z6V68zSq7pIh(aI=fy7HNF+^eml072XqkMjacmp!OlrR3kD`v1;AaibH9payCKokN| z2t*;KXOW0EAl`s@1L6(H(+Ol2jm)BvSu`?>h8Ee886z@dL}rZBSrOt5h&LeKfOrG4 rR)DM(AZrEFI)He?|DrbtWTT7yicuj(D0efZExQRjmy&Dui_B07 z36pA+2t~+kWQW}4em~z@Ym{XF&pG=!=RD7Op4EQr8S}mC``+)p{NDF_-?c`tvEj~N z<}RI!!C-#T-L>5WgW;oqpA^A4;1d&x!94JSbu-zijbXlB{ssIJNHN#7H!#4g1MdYf z{8&*89wGt%VX#Xv{P24WMi0B}+j|qNERP0ri&wuE+cXybe|20;q z7LHF)M4CjyV5i>Dt;L5%`o%SNP5TTP?A}u#i2X*#dt)P=7$K49bMgBG$UtJmpEF5m zY!XRsBWZBW%mI?zYWz=Z&}k<2v`*bAwx+J3ne3P|gJUcXkG)xlsSTew2lggo{*Rjj zv&_NiMPr{3gk}!tNl5%42WY|hd=W&vyOHS3fe0dTy2xPOmI%sH=Cs7GnK>X!vHMAq zN+QMu^Gh^zXAT$_yqYOT5-MPxlu!Zl%n-F;1OVtLL?GyhLIesCKZX(%B2b9ZO@_DgWGQcVVxYHM=(!Usn2$~#Ay^IKOX-X&DR3XVE zM%-Ofv=VBB++@fLNrchGW*MW4N!c>jSn9r_%{L$XX+;xGWZ?W)?BH3(*ukO7WKrfC zJJOu{K5)`zEF?==r|SlRG)b;ePkW9Xcl#`3+!y(m<%P(iU?(3m;GWGL(BFx?u^C!D zF~U`5C7wuKHOml?v^>W09sv^0&hNcwz1fuC2lee&`#J-2yjO!l4f%2;-(;I^#~%odBfSp z41F1y7pc%rW>J-%1ibmbRiiGv6I6{eRg9_;^+wbiQBXxe_2;;W#zr*7K~o$wQ$;h? zpARGGfdf5opyx*P_>Ug{f3EPLRUEX6gBIidO-&Uo#-TM;v^4&6*&Hp6qa6;k!+|!c z(0=33y-2iCg*K}Gwox_x9;R;4@AhJUHAU`uggevl@bupO^f7hNt(Hs~i-0?$)1I81 z)_MOv2_O;%{c(ZyS~4>qhMd+n=iPt{FUvkdGik!UG5YDD%xPWk{~h+n!1J?{lim)S z>oFJ-26||=qxOPX^{WSSN_r)Rzn9+O$Arl2;_tMKnA+p?nUtc+KZ+|j8m>cl5AnZ`Naf$3@I^_CT|z}!b~GLZ9}$IvVf)X`!W9NuRO6?=7e`X3gGSeycXOt2Ze<4QyM7B>Vp6yAQfFk_CNq^o%^^Y2!snAY7X~u&sJ{TXz(sBko#R zCjk>>pYR#w+BxNgr{2>VPTxRIYoQkpvOOmrF9beFIu6+G7&)*4s30mPCfj(2>~%Bl<_2cJ#;ov@JjAZ59%e?COY8NQS#>@CP}tc{~Q@andh21nkt1 zvsP2WVwBL=bSm4qz}ngLL$DP~U?h)=dG}`NZ5O^@KMRlA5?2|p(S4dlKUCsxJ+Ch({rVwSZ`&_r zHXXB&{1;UCVL~W-*h=P_z=y7XWJ87f=C)biOq}D8a`K<4&~Y-bZj32fif{X8Hq^B4 zY(LAy7_VZU^^a7Tniin#;!h54q4)nY8*+=x8zIZxp)b8(`;Syu`BCetYHrf@KK95z zv!P7AL*q%mmXUt>@58|QthfYs)_AycZ`(Ftr#bKNjl%IhxLVTY^^s5i9@+1JnHc2t8}-V5IBC1C49;ttd~`2f^z&9K6cJ0hq8- z7lF*cW^AK{ddVHr$bH#gaXttN=}ASyCYa~VcDcymb_eo*CAoZ2+Lz zA3(FeO8Q{{aXVT?-T^1HDQL_ zi8)2gCwPC2bi{t4x<}uxN8XTJz<#<$EHvm3Y3VuxW~mmQN#>#3q*@`+ghGFJRLg>= zLL#2}B-!4mKZ+L~T}9oZp>XPyu>NPfw4)o11;tS~<#Eas;G|Znt$nyqy2KPP@vzg( zz-IIEki5%M3rx~|!&+0n7(tWLBwNbxjg^HvXe2GLcrUH`dT z4B4DpxEfgw${C8F9HvR2BdT5xuy(IzakUlOz;A^O(~EY@dHnX&Y;GD#AyrNwO#`vF z@6N-0qUN0HgIh&LYUm;aLnso$Vqt#-WVL|on*poM^Guf!xa25hdBs{7ksV0We1$X8 z8D~6k8h|&AR;Nh5C^(O)`HRe(2$EcQA6xAtkeRVHNE~uGX1loSsUVe+cDi^*NlRyJ z%xK~MR5%Z5q2K#?*_xX6v%DC|Q^Rh6AeBWxxPCym<4Y}8(jT&&n+eDE1zfbZ^0wfu zOv(o0bq3;X1$j?vMde(fSpMbr%eR`FnyQrJk)0Z62`uM`PG^jpm^`p8X5=0V(Rd5p zTZ5|zqm)+56e_1KD^Lm3^jD`UAPeq4ii?Kk%G>N2jyE^A>z`ka~2NH8LgZI~Di^wn8Fz z6hT60Z7aWqx|o`1)e68-67p`CgBQQ&xMQqGiOTl;Cx&3_cho${^5 zs7R(;34l0(bqh4V$(1H~=i|>7S5)MG*SqAf)q+=3hR{NHn8keSGzBUCYBtFy*$x`s z2M|@=8~E8H0s_8eQi!&cbcGqVsvwL^Gm4<+rQ0gk2prlM@ac$^|CMhT(fjkl>KYnC zyyOwY4QapsN3f(@B&6+G|h9tIxf1Ob>J0I;&EbwcX%GSUm1h0iTle}FhNwDJQVaQ=xG zw(uh*o(1#iL@QimJJQD|O1r|1zK;G?C2+dQ?dw&ZtJpwZ`vWL~IS<=s<2MLowSO0M zy$^d4u{h|irgRAp?ew{6>>)pj>ZFJ9(CD_SlNDl9yZP#Ev;2H!P+^YZMD8c_8vP3FFK;>X-TKBGLpTSRN z>;QPPGNbfD?*OWKY&tJAaK^&u26#yb zs)4B-4K*QNvWhw}52ENO3L&_R!st}^_&*I~HvaV6w{L$2MdXnD_b1erUkVD!2j?iu z&Mx6*EpR{sXVPLVRn<|-SXIZT;_(2fdPOb&p@)<+d#)!0_i4!Ea&GeS`hTS7_(#OK^*n^fs#$$j(jTD zD|QrD@r=g#O#ugqm6EtUi|a51};3=#X}Zi4oe((}w4$42bzsvurc?g7U3 z`G-S?VUjA{)g#Y8fa)SXm{wK8x^`i-l2PZ*Z;*aOrVUCx=qg}p$JQ9ANWk||km2La zSoVnNp%dt3b)i>LL&@8t?>=1n6%@%}(u|v)2K( zqr7T%gl0M18V-;93~iVF5}YE%ulHSmaGlU=&igtZNt;i2aAKuU=ykbg(2Vil2cH~p|8dHQ;x-n`C;E!cKS!&eJys40=1IZI*>dL z^%=lhJj2eodY5Lyq>-{e+^wi2gTpx#x!L((vCs=+zre|0if473hhgX0b-hn~vYd=! zVH&P2&_MokNcE|66vrBe8FuH*Bo<+!tjF_k=?+$0nD^wP{Xbr67`Byinjx`+@Pe2mnHx1NZ zU->q)3T`VH+g5PXQEfnGitaX`GZITc^4ZBrM}0%4qTh+m(f;PS6u@S@xfme213+Sl94Ypd;icoYG11t}AodL$4#qi*p^muYy>pBMc0v2FbW-X3#- zM03~sZikbalc`LLGdv6gNf&LeHQQIHo2J_XgNrvu&a)6*rETu=ANLKUL|KNosM zW&V`RL5YlEh)7M5&r)XfCXwY<=>nDty`O~ggJ7*61T~l(ehj1`l3=W%Q5~+@!lsQL zD7j?x3eO|W7hh#;fF_|o#u*v0jOGuHo)P;RD0YH;&2msLLc%vcXW+!BpSAC>iWFQL zqZa^G7nG;L=Iu%5eh%gid8WHPP=79zov#*<5aQP{INntoS1u0giL?ilM@sV!EQWUF zyT1GDjqaYFmUR;3*DdrHfDcP2#le?Dv46t5b_eAk_d5?%V7rUKDH`#0b&a=Pls9h5 z#Zhi84e{IX_|oWM-?zxu32y<)*s`~3DQxWq^h<=(s;WL@-8BbglubRksRs%7m;T%7 zU01bOU1GYuQz+z)>=AyErSvjqs41is`R3om6mV9ex+=h8suMpa_Uw5>={_R!Vkus} zk6k&)Sv>k_)aO16?zYmsflRzr(pEuQAG$$`6?~Gwkl&*DrGQiZkBzsuR_fOR z%NE_DbjUC0Ux3xVk1WK)5T!2^Yk2#mfxlxMcb-#IQgfw1e$G*(gS`Sc=87ceB4`Eb z;w6OB>FL7$ey>@IU+AEH*?vY_!l(cmE7=FEGwmw^ZOx;v+fVus{*sRm%X&b`k+;}j z^{{8b?#3J<0;45+0C8&Thux6R9Kos%FA}ueV$(gI^TBl1`GBvxM>3M^;BIgG`Pn7` zhktIMFP`uhbHnh*a+$~)&g?tL$Z`v>*CPykAi8919MC}^qeT`pdd)o?VPSwE@$8P+ zu$R$ab>ph7e2=4}~Rvop3t_}ZKBD(O(%Qttz+U?luej@@ng9)>Mv zH@a24V<~!>@ZyAaG&3^Y8Jf<#rsY(pnAf)5aU+?fEP@^MS<5=WcXnE0i|;fl?7 z#Y+mU^j(5GgrEvJ-xB~=FB`pvAcXs`z$foNQGvtV zey*oFT+gw=YbgDVEYuddKdfI$J^%_Pbz4IGjAa{jXIU)H5adl~gy-O~wgoDhLaQ8onZ{ubk}Mw|HWF zj9kg^qyCNhf&1I%_9m3gvu|QHR0^CW#z#PMNK67tJAO`vvzaK6`SnT>`rQ)BlIm;v z&ySawib6mpGDH7r63b&w*<2aDaZq-n%Qz=T^>L+m@noYGvYF{+mxJ?O1SiIfzx6Ru z42#YJfp3{b`+3%&@g&s9o>mhDoNyI!uB$xNGOAcq+fJ(Ohmi|@E>5Y66BKg)G1!8P z%|I#p@uS6X)+8nLBAA=;y5&A0O)_1H(j<9q*%&8hs8qA94dLPn*w+D_WcXVjG!?qP zZZGkp1TNbcv}~j1Kt@7g??Fk~@0{;ZtHj4H(o0aHx7 zRNXcD=?!c6G8{j^cj(AVSqDXWp&E6S`PpTOey0ao#! zchH3-)a^mf$`T;!t+A9_xhq<6SLWU#jwYx-!g}!sp5l&!x9Yi)3ZW`!m{VVZ0Ct%d zNQdO4BT9u}QFB$X9qq&kSk042C7y`TIQR$%O_>rp07-7w&d=5+!i1OVC`) zoMz&H!c;y2b%EaG?L=nV&z-2-sw^a(BTnk}=};Yzo1!`Zs6IZQgtH3JeegVASKHaI zdo$cg(wplDgdzi4VXu*5-x%O-`2C9`kaN*MA(g83q@it*%g{RR&;hpc_3Fw?Q&~j< z2xVFZzQ4nA1ZB@JE(u&4PKxdC`Rz?P#YO~8C|Lk`8VJ4<(mg`3v2^Qmn_0>oR?3Zk zNN9AQm8|R-@IU^g;(c%^vYr$)=PuvV@SINr|%P1;}!% zhvp(;UhL`V>3;KtI5R>d!bwr~^}W}XN2Zt@1(bt+zR2Fx=Xu7o=V1S37Pf~X=@KwL z_NW1mumznqtR$_G8>n*mIOCL>A+nOGj9TW0Ega2H9jpKimm&BIYy_VYq$9kH@+%OA zU2u(dy#oW!Sj6FdYc;w-pVaSP(!JH;zfU50U>B4JKI=h%ygQ0!!zm|LJSnhi>XHxh zYOBg?MyC0J&8KoorzLU=*2G3{-k$lF?V!BAi>AR4^7jjdjDadw{+WvyTixO%&loY2 zbL}@sI2R6Ob2Pc+s5<|U?)}KF0Gu?fUTKYdm5Y?1XO`2mLdKy28OevBFwA}Z>=FcH zG`0Yckuc2{hEPG@9RRr%vTEy!tm`v|+hhzU{6dfOTVJo(Sqyh>L45B5#oL_bW@{wN zJAxgIt}>%azsfY!sL^Wjx-^LVwT2-mrLGRlL4Na8psFpO%yIzf%Zt-AHWYk`LCm-Ucl-Toiz8XZ zg^vc8F3r8SY4$^*;8W2hsyZtO85J3SJR5Z1_x7fbP2as`!#N+%jtj%~lko>&blv?N zImDVQpu4~ZM$h=X&G5E&!`5aTDQqb!z0PA*8XP#o8=V#-0@kysack=7$zb?+)YN#& zcj#MxX&CkEY`t3Qb6sGkVo3ZWULv0fHfZSrnu^dqRQWmOLP5{3hjA8dB4?jot9`lD zk$KL`jogR)-H9*|jdef&CIRy^6wA(VI8^h#v7zCJv766mcRfoz^BQN7QbHEazOSGq z+I3MOblztM4%g%~yC5s<1}J#zaVBD0V7cJ_jJ!j!c2W!SdxZx5CpTtD5V(zVk{j)O z$0W!)46R1K@#X}d!3WV?y;xV5OW&&zl4+6wJ34l@5WNrey=eKR;f?Ak;$$rBooj$L zL{klPVAO+GfJw!V55L#Bpn8lEZrBsArS^y557_wWTGn%{d8L-$~r1_)L>9I zMG*!CM>Hg%(G<;LX5?9D`h{kWXuggfHP8bddNxIm)o3vTt;0;KO`+8&QB?jQ67F`6J%5{e8ipVc zm;~%!(a{3`gx~I}AP|Jf<>pO&x0{+bz1+PJM&6I?95fv~9S|;dhMMXi(2e+b6H8|v zlk>3GHRa-$yCeE@bDuL&WM5Rc8}>yG&r=-AJw_#x-7C}*JC?_=F2dKQ_TRdQ!p^I) ze0(>NJaK685h~zXxDqFsRiU2+i4fW8-PL^am4kk%=2|%+8ShrtHTruNvl=!xjwFqr zPQ*)Cyc`z-OK`oHocT6c-ZtCPqUie()q4ijD=GdgMJz>_ihHW5VR&Vgzjb_YyPACH zwDewIlPY>FYN+T&=cP#p5QXhindM`M+Fm>TpzRKQi;_ErfAU;&CCH33gZWokPg>H| zXzN=NA_Y$=EhEvV<^;DNKu#fljXYD9eOD}csq0pUTw%Jd;J3Pnz3{4AV{R=Y5qb&b zS5W7P)cEaGF~rNCPgrm!)1$u8B~mWspI8nZYFy~o1VNUB+9(9W8mz_6B&3#S-Z#HM zzOPX{uRqO+g%}a&_ffgCm87v>DPxUc@C!4ng?q!%g;zvKd_dpKHn;&^0yU+(YO7I z-~QIFzZo`-Zt}ST$7!yoLei#{YwGRcW|h#lEY?-N;R%zY?K&gLom9969M_C~GGFq_ zLs$#jtN0H1$H<^%wktNQ8j1YOE}<4S*?A0H^UiPPbDZDo3`{H&n?!$8K-I>sl=h|h zzANgvhl0O)3z}A)O$2Kh!-AX({d(ygy#@Paw4}A3N9^!iN}j%QK_Hv|Ikdyb{;7SR zqBfIk+sjKx`;D5u{vawrL!5kEZQOi-S}VoL{?$Z9&nIo1Jj+neP>)+@%Alu}BFfV& zj&R!)NBE9h@V)HB&kdGZ&&fYvq?`()4qqBEt(IN7{@IiP^})-c zMRP+%*B+K5Y2yBYtm!V8uM32#2_(=t(OT?JlRH$OyLFJC^9hGfjZedVb|Y~J zoDcDFyXAxw!Wv=i!-vFay7mXHUUwsvWdFl3>ym0Pic^0uB&jT7AEnaN++=@OZ=UV% zNcu<8!T$1vtnnO{1lqPzFZ)7N(PNH$R_e5v z52_THCyJ`Ks3OP3OHa?-2d%w zQ;C46kI#capeHUM$R8VBU_JUJ0>{xcCo4D;{I3O=lu7;X9`x$S^p?PuD6r6b-?@(j zf!KwP4k|6f%fCP%);C%jYQ}+7D`VEq56yx>zd{`CYsw@-RJ6~Be0&|a=J zZ|qgiyso!$x15=fBkCaF@+N5UCK&DhMq20YYdCLu)byij$Zpl9Kn}B%!1YLYb^m3L z+G%NV&3{^#QiMtzzKn)B*Fn{(tj-I|;@DiSJA7AN_1vb1YxO-cI5bGgLHvIfE6=$rl-Xijz)hpd5rj;hA1w zhyKwR)VkqvGLFOHw_3FbaoP7nN%xnF%HOJs3tpX|iIV~CM!ohePfw3m0nxLu)%uBU6^d&OZ=C6HUT>P5f{DfJW949n`946npSIfce9el6h#lT$~Z!lKe&5f)u zhw`4N(s&F)hcSe%cSL=!M}1jsZJSJJOfMYY}_o?aaaWF5Wj#L?gvwl=A%v-*0cBI)af zjPoH@U&fh_(*No+DrHZCY)NV!O*M%cn=j@yGVxdxfZGhxEPo@6(|`fSX!JN;F|txp z$stX-vZ8g!M%~yprMy{NhO;_QsB$_YMOTNu(6Ukd|DdZ!VlqJb4S z50uhfXX1cNB#wAWqf}=TC|(l3_snYx<(YllR!v>oX|nES>t%Z(`R3~D#{!M^nI||5 zetcdroj?H~NQD<|)c2>4h1=k>&Yd};u+cjcqcu5Ra$F0VGCwz~J&jv8vISXK?}vX~ zd6_Wg)ApG#;$edM>l*?8)t{KBOa;CvW`pDX`s^$_aJAf7^M?;9$+ul=?Rx9!yQ5(# zdy3>B(%y=kOv#vw?V|s{3+R#{nwij2Jbv?`Ae*#O0&4%Nb0Z5QN)ALYOLZ&x~unQVx2l--m|~2a-Go(n7@O1ywd?DlUp?Kyf3sWs5fBlrIM-S% zT*QTE&Vsr}{ApJ=bnUbqrg6({AnP;%!Alhc z23MN&Y|C3-m1*YR4sUa~T=nK#p$saX$cRlUjiVE@svUOQk`&410h`nAPWz%>Rq)wS zEJIKqNmba{UrQaO_qeg0?g%?GcgNbG`3-VcpMK%6)^*8}a=UO~H*l&obh6%{ z+3v*cV|WU9W}3)ZVMedW>$imvM$GMg=fYb{PX_#JKm%UskqQ&f*)*py$+u;sxfDPV znd@!M<;HvU|N28r{Q61obP-BcR)O)g4DiA(4-k8gMz8CMkIV!xE)?;`gneNIH5ysa z2IrsC@6IXI(V->T^)95}InheSd;BjarD9Qg>EW2vZEk=CoG;TLm~Kg-0_uEdNlWKQ zMMO9iXS10aNQjXAl5CjlK<(U1)UTDZUROkk?|cJX~_medlSC`r)3&pr0&cp@ECg! zL4U;DSg6#CSbzJzKVMM+Y>TBGtdPgVbEEgVcPjJ0wTy-2=H)>rpKmc68)IGRHX#Vz zl`%rKyti~33;z{E0O(1Jy72<=OkHrQlnyE8M?QH_)bSst26%Tq#^N_Cx%<}>adj}H zO27s;PWv(o@cYh?zxzd6TueUF$4SA^!wj2JjmHA!V31MZAr8@f3{N8e{5_Ova8&k268#H@-G;G| z50VLG@b2RA#)Fm^*1{tVJBRyT$9cwo__GN4Lsfhh9J)#f;_Kd97m?F{4LH~5MjKNq zm$?ch701-z7DoVx-dMQUx4qkU2jA>eo4d*EvE4;{4m z`tKEt#URW_6}7u*PpI2(%7}>}bLFqyD-zitoShi7+lo!Y_fUtj-VZ2mbAJsy9{~Ke z*qFk$XC-rXTCm({ySuQ~X*(U2bOCK}HMI2H+_ks;flJ%tE0z#Bj%dto55}Sn9YVPo zJl#H4(q=sZ%?h;I?u$Rpl(A^<=eAd&MKXaU&rK|wg-c6Ido3+371?}h-nW+e>7u6Y z$76OAjy&uo36hohtQRmvULhK~M_Kd(0!a`3oPV>(_OiDUI8PV5$O{V%R1khw@1wLz z<^@}yi+RAd?Mzk^gsBto4z z#Ko~@on`onnE=r{HmqpOO@-9=^~Q1lw49R>8JCXf1I6mSyue)?`$S`6zAs|M_=PIZ zbMHt4qhssu%?S?8Y_Hhj${|j!7z$ z2!5t=z;1fjOZRr5A$H?MxWe@{y0`4$I!w%H4Q!~E=WR^>VBrq-s6e}t6GPv;9&Is4 z!2#*O-x+JT7;6JD;6t~kS&<%c`0(QA9O8gpIqFY-8X)F;4-0}=DL!z!25*0$=Pygg zT@nxPPQhGzO@ryKPSr^ty4Q+4ilf&IeY4mlf?Ur~82Sp*jkVZy6qFs;%J+_4zvA~f z@ze9v@a30dd#iZ6*|9I`OO^_LO2@v-xyM7! zn{OavQ0h+?y@NP91hg&_2-rVdqHAh@&sm8vF%$gtX+dU9@-w^F?*vDuI0rVt zV_=xj-`F-5%Fh^Pm^Kpp;INh#B$PBPr#?|F^y|~2%%>>}1r^6BS}q?2la4ZjmSfqT zCp@;(BgS}S3WLp{MdMyra=NcM&7;}+?z^yY*Gr3zuU%Ul@>Ge-G(E?&3_1+B$L3wr zI=}PnU5AoV-Kys7V+|zB#rkLQEE|$E2;wZP8-*$t;`1^!5ERx7^)3fnokW*%8i-D} zpegwDXvk_^szq^HUYzf5?zQ;-ge+O9#QUpKW1J{omra*V57N}=Ah-2s_tb6R(`nNw-8m3yRDwj|*yZ4ZTA-{AB93+wsL}gNF(% z&we9hdwFwu`g2FwGVE3CCj99bDdFS(Xgu*}3NS&ca_(9(S!Aa2ih|alnGp>sm+Uq&P5t;aspS>U)*OL>1o<77a^DH<{=#? zZG&ce|GBA}FnG$J@LkQCaeOrhma${yKIU?bdI`)8a8si>o$n-Q=j;Cd6TJ%+3Ae)C zE>nR~EOk=j0#<=?|GXLw%wXLpyUeSa&NsP#{mCSwIPwgye=s6Y`7CQ94s44W> z#nfOx2^M97F1hA?Lk%dbtpAyUPe|%d8=a`vF);rB%&`XknQ8y`XX9_Ri9;&yipdwl TmYCV2FL_$GbT!_q+x+!Ef07_h literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsHidden-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductPlaceholderViewSnapshotTests/test_productPlaceholderView_whenIconIsHidden-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6b85ea1cf9224b181c9718ab005a54705b0fb66d GIT binary patch literal 37695 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

  • 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 Cw|KVz literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_error-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_error-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6b85ea1cf9224b181c9718ab005a54705b0fb66d GIT binary patch literal 37695 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 Cw|KVz literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_loading-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductViewSnapshotTests/test_productView_loading-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6b85ea1cf9224b181c9718ab005a54705b0fb66d GIT binary patch literal 37695 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 ?R!wwvq@Sc!T~s4Zd0#o#tbQp$eM0sjIC1tf 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_error-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_error-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..6b66ac4b1b457547259b301f80f618068cefede3 GIT binary patch literal 81916 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 cI{j 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products-macOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products-macOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..52be0f21b21782c8abafc183956668c5447c172e GIT binary patch literal 155791 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 l3V;Hj04Ugj0vihCF`DM*+R5dUVR8{ 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products_withRestoreButtons-tvOS.1.png b/Tests/SnapshotTests/__Snapshots__/ProductsViewSnapshotTests/test_productsView_products_withRestoreButtons-tvOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..2e869505b7c40b1f267a075ffc03f6a480f725ac GIT binary patch literal 50562 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

    ctNn>Erg(aw$>boNp2i8gH)0*2DbhHAQ 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 literal 0 HcmV?d00001 diff --git a/Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_customStyle-iOS.1.png b/Tests/SnapshotTests/__Snapshots__/SubscriptionsViewSnapshotTests/test_subscriptionsView_customStyle-iOS.1.png new file mode 100644 index 0000000000000000000000000000000000000000..09e4c36e052d5a5b17a9850f8113c0b7c80a58f7 GIT binary patch literal 105315 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 tC2QCCC#1lTP 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 GOdalvt23vq^A@j}txv;T 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