diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..919434a625
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/ReceiptParser.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/ReceiptParser.xcscheme
new file mode 100644
index 0000000000..88bcfe6ff1
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/ReceiptParser.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat.xcscheme
new file mode 100644
index 0000000000..42d4dc01f0
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCatUI.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCatUI.xcscheme
new file mode 100644
index 0000000000..bd9124ed8e
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCatUI.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat_CustomEntitlementComputation.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat_CustomEntitlementComputation.xcscheme
new file mode 100644
index 0000000000..db2ce1c0c2
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/RevenueCat_CustomEntitlementComputation.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Package.swift b/Package.swift
index fdf3f75d7e..2d2bdc779e 100644
--- a/Package.swift
+++ b/Package.swift
@@ -10,7 +10,8 @@ let environmentVariables = ProcessInfo.processInfo.environment
let shouldIncludeDocCPlugin = environmentVariables["INCLUDE_DOCC_PLUGIN"] == "true"
var dependencies: [Package.Dependency] = [
- .package(url: "git@github.com:Quick/Nimble.git", from: "10.0.0")
+ .package(url: "git@github.com:Quick/Nimble.git", from: "10.0.0"),
+ .package(url: "git@github.com:pointfreeco/swift-snapshot-testing.git", from: "1.11.0")
]
if shouldIncludeDocCPlugin {
dependencies.append(.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"))
@@ -30,7 +31,9 @@ let package = Package(
.library(name: "RevenueCat_CustomEntitlementComputation",
targets: ["RevenueCat_CustomEntitlementComputation"]),
.library(name: "ReceiptParser",
- targets: ["ReceiptParser"])
+ targets: ["ReceiptParser"]),
+ .library(name: "RevenueCatUI",
+ targets: ["RevenueCatUI"])
],
dependencies: dependencies,
targets: [
@@ -47,10 +50,21 @@ let package = Package(
.copy("PrivacyInfo.xcprivacy")
],
swiftSettings: [.define("ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION")]),
+ // Receipt Parser
.target(name: "ReceiptParser",
path: "LocalReceiptParsing"),
.testTarget(name: "ReceiptParserTests",
dependencies: ["ReceiptParser", "Nimble"],
- exclude: ["ReceiptParserTests-Info.plist"])
+ exclude: ["ReceiptParserTests-Info.plist"]),
+ // RevenueCatUI
+ .target(name: "RevenueCatUI",
+ dependencies: ["RevenueCat"],
+ path: "RevenueCatUI"),
+ .testTarget(name: "RevenueCatUITests",
+ dependencies: [
+ "RevenueCatUI",
+ "Nimble",
+ .product(name: "SnapshotTesting", package: "swift-snapshot-testing")
+ ])
]
)
diff --git a/RevenueCatUI/PaywallView.swift b/RevenueCatUI/PaywallView.swift
new file mode 100644
index 0000000000..71cb83e5a6
--- /dev/null
+++ b/RevenueCatUI/PaywallView.swift
@@ -0,0 +1,64 @@
+import RevenueCat
+import SwiftUI
+
+// swiftlint:disable missing_docs
+
+@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
+public struct PaywallView: View {
+
+ public let offering: Offering
+
+ public init(offering: Offering) {
+ self.offering = offering
+ }
+
+ public var body: some View {
+ VStack {
+ Text(verbatim: "Offering: \(self.offering.identifier)")
+ .font(.title)
+
+ List {
+ ForEach(self.offering.availablePackages, id: \.identifier) { package in
+ self.label(for: package)
+ .listRowBackground(
+ Rectangle()
+ .foregroundStyle(.thinMaterial)
+ )
+ }
+ }
+ .scrollContentBackground(.hidden)
+ }
+ .background(.blue.gradient)
+ }
+
+ private func label(for package: Package) -> some View {
+ HStack {
+ Button {
+
+ } label: {
+ Text(package.storeProduct.localizedTitle)
+ .padding(.vertical)
+ }
+ .buttonStyle(.plain)
+
+ Spacer()
+
+ Image(systemName: "chevron.right")
+ .font(.body)
+ }
+ }
+
+}
+
+#if DEBUG
+
+@available(iOS 16.0, macOS 13.0, tvOS 16.0, *)
+struct PaywallView_Previews: PreviewProvider {
+
+ static var previews: some View {
+ PaywallView(offering: TestData.offering)
+ }
+
+}
+
+#endif
diff --git a/RevenueCatUI/TestData.swift b/RevenueCatUI/TestData.swift
new file mode 100644
index 0000000000..34934997bc
--- /dev/null
+++ b/RevenueCatUI/TestData.swift
@@ -0,0 +1,72 @@
+//
+// File.swift
+//
+//
+// Created by Nacho Soto on 7/6/23.
+//
+
+import Foundation
+import RevenueCat
+
+#if DEBUG
+
+internal enum TestData {
+
+ static let product1 = TestStoreProduct(
+ localizedTitle: "PRO monthly",
+ price: 3.99,
+ localizedPriceString: "$3.99",
+ productIdentifier: "com.revenuecat.product",
+ productType: .autoRenewableSubscription,
+ localizedDescription: "PRO monthly",
+ subscriptionGroupIdentifier: "group",
+ subscriptionPeriod: .init(value: 1, unit: .month),
+ introductoryDiscount: .init(
+ identifier: "intro",
+ price: 0,
+ localizedPriceString: "$0.00",
+ paymentMode: .freeTrial,
+ subscriptionPeriod: .init(value: 1, unit: .week),
+ numberOfPeriods: 1,
+ type: .introductory
+ ),
+ discounts: []
+ )
+ static let product2 = TestStoreProduct(
+ localizedTitle: "PRO annual",
+ price: 34.99,
+ localizedPriceString: "$34.99",
+ productIdentifier: "com.revenuecat.product",
+ productType: .autoRenewableSubscription,
+ localizedDescription: "PRO annual",
+ subscriptionGroupIdentifier: "group",
+ subscriptionPeriod: .init(value: 1, unit: .year),
+ introductoryDiscount: nil,
+ discounts: []
+ )
+
+ static let offering = Offering(
+ identifier: Self.offeringIdentifier,
+ serverDescription: "Main offering",
+ metadata: [:],
+ availablePackages: [
+ .init(
+ identifier: "monthly",
+ packageType: .monthly,
+ storeProduct: product1.toStoreProduct(),
+ offeringIdentifier: Self.offeringIdentifier
+ ),
+ .init(
+ identifier: "annual",
+ packageType: .annual,
+ storeProduct: product2.toStoreProduct(),
+ offeringIdentifier: Self.offeringIdentifier
+ )
+ ]
+ )
+
+ private static let offeringIdentifier = "offering"
+
+}
+
+#endif
diff --git a/Tests/RevenueCatUITests/Helpers/CurrentTestCaseTracker.swift b/Tests/RevenueCatUITests/Helpers/CurrentTestCaseTracker.swift
new file mode 120000
index 0000000000..9b678103f1
--- /dev/null
+++ b/Tests/RevenueCatUITests/Helpers/CurrentTestCaseTracker.swift
@@ -0,0 +1 @@
+../../UnitTests/TestHelpers/CurrentTestCaseTracker.swift
\ No newline at end of file
diff --git a/Tests/RevenueCatUITests/Helpers/ImageSnapshot.swift b/Tests/RevenueCatUITests/Helpers/ImageSnapshot.swift
new file mode 120000
index 0000000000..651268cbe0
--- /dev/null
+++ b/Tests/RevenueCatUITests/Helpers/ImageSnapshot.swift
@@ -0,0 +1 @@
+../../StoreKitUnitTests/TestHelpers/ImageSnapshot.swift
\ No newline at end of file
diff --git a/Tests/RevenueCatUITests/Helpers/OSVersionEquivalent.swift b/Tests/RevenueCatUITests/Helpers/OSVersionEquivalent.swift
new file mode 120000
index 0000000000..ad8dce8eee
--- /dev/null
+++ b/Tests/RevenueCatUITests/Helpers/OSVersionEquivalent.swift
@@ -0,0 +1 @@
+../../UnitTests/TestHelpers/OSVersionEquivalent.swift
\ No newline at end of file
diff --git a/Tests/RevenueCatUITests/Helpers/TestCase.swift b/Tests/RevenueCatUITests/Helpers/TestCase.swift
new file mode 100644
index 0000000000..806c222422
--- /dev/null
+++ b/Tests/RevenueCatUITests/Helpers/TestCase.swift
@@ -0,0 +1,55 @@
+//
+// TestCase.swift
+//
+//
+// Created by Nacho Soto on 7/12/23.
+//
+
+import SnapshotTesting
+import XCTest
+
+// swiftlint:disable xctestcase_superclass
+
+/// Parent class for all test cases
+/// Provides automatic tracking of test cases using `CurrentTestCaseTracker` as well as snapshot testing helpers.
+class TestCase: XCTestCase {
+
+ @MainActor
+ override class func setUp() {
+ XCTestObservationCenter.shared.addTestObserver(CurrentTestCaseTracker.shared)
+
+ SnapshotTests.updateSnapshotsIfNeeded()
+ }
+
+ @MainActor
+ override class func tearDown() {
+ XCTestObservationCenter.shared.removeTestObserver(CurrentTestCaseTracker.shared)
+ }
+
+ @MainActor
+ override func setUpWithError() throws {
+ try super.setUpWithError()
+ }
+
+ @MainActor
+ override func tearDown() {
+ super.tearDown()
+ }
+
+ // MARK: -
+
+}
+
+private enum SnapshotTests {
+
+ private static var environmentVariableChecked = false
+
+ static func updateSnapshotsIfNeeded() {
+ guard !Self.environmentVariableChecked else { return }
+
+ if ProcessInfo.processInfo.environment["CIRCLECI_TESTS_GENERATE_SNAPSHOTS"] == "1" {
+ isRecording = true
+ }
+ }
+
+}
diff --git a/Tests/RevenueCatUITests/PaywallViewTests.swift b/Tests/RevenueCatUITests/PaywallViewTests.swift
new file mode 100644
index 0000000000..ffdf848764
--- /dev/null
+++ b/Tests/RevenueCatUITests/PaywallViewTests.swift
@@ -0,0 +1,16 @@
+import Nimble
+@testable import RevenueCatUI
+import SnapshotTesting
+import XCTest
+
+@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
+class PaywallViewTests: TestCase {
+
+ func testOne() {
+ let view = PaywallView(offering: TestData.offering)
+ .frame(width: 300, height: 400)
+
+ expect(view).to(haveValidSnapshot(as: .image))
+ }
+
+}
diff --git a/Tests/StoreKitUnitTests/Support/DebugViewSwiftUITests.swift b/Tests/StoreKitUnitTests/Support/DebugViewSwiftUITests.swift
index 6df9049357..eb488bffaa 100644
--- a/Tests/StoreKitUnitTests/Support/DebugViewSwiftUITests.swift
+++ b/Tests/StoreKitUnitTests/Support/DebugViewSwiftUITests.swift
@@ -32,7 +32,7 @@ class DebugViewSwiftUITests: TestCase {
func testLoadingState() {
expect(self.view(with: .init(), width: 300, height: 400))
- .to(haveValidSnapshot(as: .image, separateOSVersions: true))
+ .to(haveValidSnapshot(as: .image))
}
func testDebugView() throws {
@@ -56,7 +56,7 @@ class DebugViewSwiftUITests: TestCase {
))
expect(self.view(with: model, width: 450, height: 900))
- .to(haveValidSnapshot(as: .image, separateOSVersions: true))
+ .to(haveValidSnapshot(as: .image))
}
}
diff --git a/Tests/StoreKitUnitTests/TestHelpers/ImageSnapshot.swift b/Tests/StoreKitUnitTests/TestHelpers/ImageSnapshot.swift
index d0f850b879..af30f29aed 100644
--- a/Tests/StoreKitUnitTests/TestHelpers/ImageSnapshot.swift
+++ b/Tests/StoreKitUnitTests/TestHelpers/ImageSnapshot.swift
@@ -22,7 +22,7 @@ import SwiftUI
func haveValidSnapshot(
as strategy: Snapshotting,
named name: String? = nil,
- separateOSVersions: Bool = false,
+ separateOSVersions: Bool = true,
record recording: Bool = false,
timeout: TimeInterval = 5,
file: StaticString = #file,
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj
index 1ab7eb309c..0d32c8baf2 100644
--- a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj
+++ b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj
@@ -10,9 +10,9 @@
4F217A102A6DB6FB000B092D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FC046BF2A572E3700A28BCF /* Assets.xcassets */; };
4F4557E22A6FFE6A00160521 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4F4557E42A6FFE6A00160521 /* Localizable.strings */; };
4F4EE7CD2A572F5400D7EAE1 /* SimpleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC046BC2A572E3700A28BCF /* SimpleApp.swift */; };
- 4F4EE7CE2A572F5A00D7EAE1 /* Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC046C22A572E3700A28BCF /* Views.swift */; };
4F4EE7CF2A572F5D00D7EAE1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC046C32A572E3700A28BCF /* ContentView.swift */; };
4F4EE7D22A5731E800D7EAE1 /* RevenueCat in Frameworks */ = {isa = PBXBuildFile; productRef = 4F4EE7D12A5731E800D7EAE1 /* RevenueCat */; };
+ 4FC80B202A5DE8E300E95DB0 /* RevenueCatUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4FC80B1F2A5DE8E300E95DB0 /* RevenueCatUI */; };
4FCA01FB2A3A1CBD00B262C0 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FCA01FA2A3A1CBD00B262C0 /* StoreKit.framework */; };
/* End PBXBuildFile section */
@@ -39,7 +39,6 @@
4FC046BE2A572E3700A28BCF /* Products.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Products.storekit; sourceTree = ""; };
4FC046BF2A572E3700A28BCF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
4FC046C12A572E3700A28BCF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
- 4FC046C22A572E3700A28BCF /* Views.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Views.swift; sourceTree = ""; };
4FC046C32A572E3700A28BCF /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
4FC882E02A5870C6005BE85E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
4FCA01FA2A3A1CBD00B262C0 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
@@ -50,8 +49,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 4F4EE7D22A5731E800D7EAE1 /* RevenueCat in Frameworks */,
4FCA01FB2A3A1CBD00B262C0 /* StoreKit.framework in Frameworks */,
+ 4F4EE7D22A5731E800D7EAE1 /* RevenueCat in Frameworks */,
+ 4FC80B202A5DE8E300E95DB0 /* RevenueCatUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -88,7 +88,6 @@
isa = PBXGroup;
children = (
4FC046BC2A572E3700A28BCF /* SimpleApp.swift */,
- 4FC046C22A572E3700A28BCF /* Views.swift */,
4FC046C32A572E3700A28BCF /* ContentView.swift */,
4FC046CA2A572EF300A28BCF /* Config */,
4FC046C02A572E3700A28BCF /* Preview Content */,
@@ -135,6 +134,7 @@
name = SimpleApp;
packageProductDependencies = (
4F4EE7D12A5731E800D7EAE1 /* RevenueCat */,
+ 4FC80B1F2A5DE8E300E95DB0 /* RevenueCatUI */,
);
productName = SimpleApp;
productReference = 4F6BED9A2A26A64200CD9322 /* SimpleApp.app */;
@@ -191,7 +191,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 4F4EE7CE2A572F5A00D7EAE1 /* Views.swift in Sources */,
4F4EE7CD2A572F5400D7EAE1 /* SimpleApp.swift in Sources */,
4F4EE7CF2A572F5D00D7EAE1 /* ContentView.swift in Sources */,
);
@@ -437,6 +436,10 @@
isa = XCSwiftPackageProductDependency;
productName = RevenueCat;
};
+ 4FC80B1F2A5DE8E300E95DB0 /* RevenueCatUI */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = RevenueCatUI;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = 4F6BED922A26A64200CD9322 /* Project object */;
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp - Live Config.xcscheme b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp - Live Config.xcscheme
new file mode 100644
index 0000000000..c17a37abf9
--- /dev/null
+++ b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp - Live Config.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp.xcscheme b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp - SK config.xcscheme
similarity index 100%
rename from Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp.xcscheme
rename to Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/xcshareddata/xcschemes/SimpleApp - SK config.xcscheme
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/ContentView.swift b/Tests/TestingApps/SimpleApp/SimpleApp/ContentView.swift
index 5dab0026da..79349f545f 100644
--- a/Tests/TestingApps/SimpleApp/SimpleApp/ContentView.swift
+++ b/Tests/TestingApps/SimpleApp/SimpleApp/ContentView.swift
@@ -8,30 +8,18 @@
import SwiftUI
import RevenueCat
-struct ContentView: View {
+struct DebugView: View {
- #if os(macOS) || os(xrOS)
@State
private var debug = false
- #endif
var body: some View {
- ZStack {
- Content()
-
- #if os(macOS) || os(xrOS)
- Button {
- self.debug = true
- } label: {
- Text("Debug")
- }
- #endif
+ Button {
+ self.debug = true
+ } label: {
+ Text("Debug")
}
- #if os(macOS) || os(xrOS)
.debugRevenueCatOverlay(isPresented: self.$debug)
- #else
- .debugRevenueCatOverlay()
- #endif
}
}
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/SimpleApp.swift b/Tests/TestingApps/SimpleApp/SimpleApp/SimpleApp.swift
index 20febe24ce..7e72bec8b9 100644
--- a/Tests/TestingApps/SimpleApp/SimpleApp/SimpleApp.swift
+++ b/Tests/TestingApps/SimpleApp/SimpleApp/SimpleApp.swift
@@ -6,6 +6,7 @@
//
import RevenueCat
+import RevenueCatUI
import SwiftUI
#warning("This needs to be configured.")
@@ -26,9 +27,24 @@ struct SimpleApp: App {
)
}
+ @State
+ private var offering: Offering?
+
var body: some Scene {
WindowGroup {
- ContentView()
+ Group {
+ if let offering = self.offering {
+ PaywallView(offering: offering)
+ }
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .overlay {
+ DebugView()
+ .frame(maxHeight: .infinity, alignment: .bottom)
+ }
+ .task {
+ self.offering = try? await Purchases.shared.offerings().current
+ }
}
}
}
diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views.swift
deleted file mode 100644
index 8b90622f68..0000000000
--- a/Tests/TestingApps/SimpleApp/SimpleApp/Views.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// Views.swift
-// SimpleApp
-//
-// Created by Nacho Soto on 6/15/23.
-//
-
-import SwiftUI
-
-struct Content: View {
-
- var body: some View {
- Group {
- #if os(macOS) || targetEnvironment(macCatalyst) || os(xrOS)
- self.background
- #else
- self.background
- .statusBarHidden(true)
- #endif
- }
- .frame(maxWidth: .infinity, maxHeight: .infinity)
- .edgesIgnoringSafeArea(.all)
- }
-
- private var background: some View {
- Rectangle()
- .fill(
- Color
- .red
- .gradient
- )
- }
-
-}