diff --git a/Samples/Images/com.rarcher.nonconsumable.flowers.large.info.png b/Samples/Images/com.rarcher.nonconsumable.flowers.large.info.png new file mode 100644 index 000000000..a3899b758 Binary files /dev/null and b/Samples/Images/com.rarcher.nonconsumable.flowers.large.info.png differ diff --git a/Samples/Images/com.rarcher.nonconsumable.flowers.small.info.png b/Samples/Images/com.rarcher.nonconsumable.flowers.small.info.png new file mode 100644 index 000000000..c451e9b0e Binary files /dev/null and b/Samples/Images/com.rarcher.nonconsumable.flowers.small.info.png differ diff --git a/Samples/Images/com.rarcher.subscription.vip.gold.info.png b/Samples/Images/com.rarcher.subscription.vip.gold.info.png new file mode 100644 index 000000000..471987c2a Binary files /dev/null and b/Samples/Images/com.rarcher.subscription.vip.gold.info.png differ diff --git a/Samples/Images/com.rarcher.subscription.vip.silver.info.png b/Samples/Images/com.rarcher.subscription.vip.silver.info.png new file mode 100644 index 000000000..0fae009fe Binary files /dev/null and b/Samples/Images/com.rarcher.subscription.vip.silver.info.png differ diff --git a/Samples/Images/plant-services.png b/Samples/Images/plant-services.png new file mode 100644 index 000000000..222b1c1e3 Binary files /dev/null and b/Samples/Images/plant-services.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview1.swift b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview1.swift index 4d7790b99..83102a63f 100644 --- a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview1.swift +++ b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview1.swift @@ -16,9 +16,7 @@ struct ContentView: View { // Products that have been purchased will be grayed-out. // Tapping on the product's image shows details for the product. NavigationLink("List all products") { - SKHelperStoreView() { productId in - // Add more content to a product's description by placing it in this closure - } + SKHelperStoreView() } } } diff --git a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview2.swift b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview2.swift index 2f27dfc2c..fd547b638 100644 --- a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview2.swift +++ b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview2.swift @@ -17,11 +17,8 @@ struct ContentView: View { // Tapping on the product's image shows details for the product. NavigationLink("List all products") { SKHelperStoreView() { productId in - // Add more content to a product's description by placing it in this closure Group { - Image(productId) - .resizable() - .scaledToFit() + Image(productId + ".info").resizable().scaledToFit() Text("Here is some text about why you might want to buy this product.") } .padding() diff --git a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview3.swift b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview3.swift index f227e0072..2c204e94d 100644 --- a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview3.swift +++ b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview3.swift @@ -17,11 +17,8 @@ struct ContentView: View { // Tapping on the product's image shows details for the product. NavigationLink("List all products") { SKHelperStoreView() { productId in - // Add more content to a product's description by placing it in this closure Group { - Image(productId) - .resizable() - .scaledToFit() + Image(productId + ".info").resizable().scaledToFit() Text("Here is some text about why you might want to buy this product.") } .padding() @@ -30,8 +27,8 @@ struct ContentView: View { // SKHelperSubscriptionStoreView() lists all subscription products for this app. // Trials, upgrades and downgrades are handled automatically. - NavigationLink("Lists all subscription") { - SKHelperSubscriptionStoreView() + NavigationLink("List all subscriptions") { + SKHelperSubscriptionStoreView(subscriptionGroupName: "vip") } } } diff --git a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview4.swift b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview4.swift index 533d2afab..90bfadf36 100644 --- a/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview4.swift +++ b/Sources/SKHelper/Documentation.docc/Resources/code/quickstart-contentview4.swift @@ -17,11 +17,8 @@ struct ContentView: View { // Tapping on the product's image shows details for the product. NavigationLink("List all products") { SKHelperStoreView() { productId in - // Add more content to a product's description by placing it in this closure Group { - Image(productId) - .resizable() - .scaledToFit() + Image(productId + ".info").resizable().scaledToFit() Text("Here is some text about why you might want to buy this product.") } .padding() @@ -30,15 +27,27 @@ struct ContentView: View { // SKHelperSubscriptionStoreView() lists all subscription products for this app. // Trials, upgrades and downgrades are handled automatically. - NavigationLink("Lists all subscription") { - SKHelperSubscriptionStoreView() - } - - // SKHelperPurchasesView() lists all products that have been purchased, - // including subscriptions. Details of the purchase or subscription are shown when - // the "Manage Purchase" button is tapped. - NavigationLink("List all purchases") { - SKHelperPurchasesView() + NavigationLink("List all subscriptions") { + SKHelperSubscriptionStoreView( + subscriptionGroupName: "vip", + subscriptionHeader: { + VStack { + Image("plant-services").resizable().scaledToFit() + Text("Services to make your plants happy!").font(.headline) + } + }, + subscriptionControl: { productId in + VStack { + Image(productId).resizable().scaledToFit() + Text("We'll visit \(productId == "com.rarcher.subscription.vip.gold" ? "weekly" : "monthly") to water your plants.").font(.caption2) + } + }, + subscriptionDetails: { productId in + VStack { + Image(productId + ".info").resizable().scaledToFit() + Text("Here is some text about why you might want to buy this product.") + } + }) } } } diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart10.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart10.png index b9e883762..b2000d12a 100644 Binary files a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart10.png and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart10.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart16.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart16.png index a91e7b27e..c99dbcf38 100644 Binary files a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart16.png and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart16.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart17.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart17.png new file mode 100644 index 000000000..d09dcd62b Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart17.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart18.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart18.png new file mode 100644 index 000000000..33d59fec9 Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart18.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart19.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart19.png new file mode 100644 index 000000000..262a05c2a Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart19.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart20.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart20.png new file mode 100644 index 000000000..4b75afdd6 Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart20.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart21.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart21.png new file mode 100644 index 000000000..b495e80f8 Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart21.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart22.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart22.png new file mode 100644 index 000000000..47f081fc2 Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart22.png differ diff --git a/Sources/SKHelper/Documentation.docc/Resources/images/quickstart23.png b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart23.png new file mode 100644 index 000000000..43918709b Binary files /dev/null and b/Sources/SKHelper/Documentation.docc/Resources/images/quickstart23.png differ diff --git a/Sources/SKHelper/Documentation.docc/quickstart/display-products.tutorial b/Sources/SKHelper/Documentation.docc/quickstart/display-products.tutorial index 500448900..9eb133e75 100644 --- a/Sources/SKHelper/Documentation.docc/quickstart/display-products.tutorial +++ b/Sources/SKHelper/Documentation.docc/quickstart/display-products.tutorial @@ -9,7 +9,7 @@ @ContentAndMedia { In this section we'll add the code required to display all products and support user purchases. - @Image(source: "quickstart11.png", alt: "SKHelperDemoApp.swift.") + @Image(source: "quickstart14.png", alt: "Display products.") } @Steps { @@ -74,9 +74,9 @@ } @Step { - If your build and run the app you'll see how the changes have affected the display of product details. + If you build and run the app you'll see how the changes have affected the display of product details. - @Image(source: "quickstart16.png", alt: "Show product details with addtional custom information.") + @Image(source: "quickstart16.png", alt: "Show product details with additional custom information.") } } } diff --git a/Sources/SKHelper/Documentation.docc/quickstart/display-subscriptions.tutorial b/Sources/SKHelper/Documentation.docc/quickstart/display-subscriptions.tutorial index 8d5b27068..c4e89b892 100644 --- a/Sources/SKHelper/Documentation.docc/quickstart/display-subscriptions.tutorial +++ b/Sources/SKHelper/Documentation.docc/quickstart/display-subscriptions.tutorial @@ -9,12 +9,64 @@ @ContentAndMedia { In this section we'll add code to support the display, promotion, purchase and management of subscriptions. - @Image(source: "quickstart11.png", alt: "SKHelperDemoApp.swift.") + @Image(source: "quickstart17.png", alt: "Display subscriptions.") } @Steps { @Step { - TODO. + If you look at the **Products.storekit** file you'll see that our two demo subscriptions for the "gold" and "silver" services + are collected into a subscription group named **"vip"**. + + @Image(source: "quickstart18.png", alt: "The VIP subscription group.") + } + + @Step { + Add the following code to **ContentView.swift** to enable our demo to show all the subscriptions in the "vip" group. + + @Code(name: "ContentView.swift", file: "quickstart-contentview3.swift", previousFile: "quickstart-contentview2.swift", reset: false) {} + + Notice how we use the subscription group name to refer to all subscriptions in the "vip" group. + } + + @Step { + Build and run the app and select the **List all subscriptions** option. You should see the "gold" and "silver" + subscription list, along with options to subscribe. + + @Image(source: "quickstart19.png", alt: "The VIP subscription group.") + } + + @Step { + We can customize the look of **SKHelperSubscriptionStoreView** by specifying three optional closures. + } + + @Step { + **Header**. Content displayed at the top of the subscription list that is common to all subscriptions in the group. + + @Image(source: "quickstart21.png", alt: "Header customization.") + } + + @Step { + **Control**. Content displayed in-line with each subscription in the subscription list. + + @Image(source: "quickstart22.png", alt: "Control customization.") + } + + @Step { + **Details**. Content displayed in the product details sheet. + + @Image(source: "quickstart23.png", alt: "Details customization.") + } + + @Step { + Add the following code to **ContentView.swift** to customize the appearance of **SKHelperSubscriptionStoreView**. + + @Code(name: "ContentView.swift", file: "quickstart-contentview4.swift", reset: false) {} + } + + @Step { + Build and run the app. You can now see how the customizations have changed the look of the subscription list. + + @Image(source: "quickstart20.png", alt: "The customized VIP subscription group.") } } } diff --git a/Sources/SKHelper/Documentation.docc/quickstart/quickstart.tutorial b/Sources/SKHelper/Documentation.docc/quickstart/quickstart.tutorial index 767aa794e..10eb284b3 100644 --- a/Sources/SKHelper/Documentation.docc/quickstart/quickstart.tutorial +++ b/Sources/SKHelper/Documentation.docc/quickstart/quickstart.tutorial @@ -26,14 +26,14 @@ @Chapter(name: "Display all products") { Add code to display all products and support user purchases. - @Image(source: "quickstart11.png", alt: "Display all products.") + @Image(source: "quickstart14.png", alt: "Display all products.") @TutorialReference(tutorial: "doc:display-products") } @Chapter(name: "Display subscriptions") { Add code to support the display, promotion, purchase and management of subscriptions. - @Image(source: "quickstart11.png", alt: "Display subscriptions.") + @Image(source: "quickstart17.png", alt: "Display subscriptions.") @TutorialReference(tutorial: "doc:display-subscriptions") } diff --git a/Sources/SKHelper/Views/SKHelperProductView.swift b/Sources/SKHelper/Views/SKHelperProductView.swift index b2a02c01b..d6e5d0ede 100644 --- a/Sources/SKHelper/Views/SKHelperProductView.swift +++ b/Sources/SKHelper/Views/SKHelperProductView.swift @@ -12,6 +12,7 @@ import StoreKit @available(iOS 17.0, macOS 14.6, *) public struct SKHelperProductView: View { + /// A closure that is passed a `ProductId` and returns a `View` providing product details. public typealias ProductDetailsClosure = (ProductId) -> Content /// The `SKHelper` object. @@ -27,9 +28,9 @@ public struct SKHelperProductView: View { @Binding var showProductInfoSheet: Bool /// A closure which is called to display product details. - private var productDetails: ProductDetailsClosure + private var productDetails: ProductDetailsClosure? - /// Creates an `SKHelperProductView` which displays product information using a StoreKit `ProductView`. + /// Creates an `SKHelperProductView` which displays custom product information using a StoreKit `ProductView`. /// /// - Parameters: /// - selectedProductId: The `ProductId` of the product for which you want to display details. @@ -41,12 +42,25 @@ public struct SKHelperProductView: View { self._showProductInfoSheet = showProductInfoSheet self.productDetails = productDetails } + + /// Creates an `SKHelperProductView` which displays default product information using a StoreKit `ProductView`. + /// + /// - Parameters: + /// - selectedProductId: The `ProductId` of the product for which you want to display details. + /// - showProductInfoSheet: Used to togggle the display of the product information sheet + /// + public init(selectedProductId: Binding, showProductInfoSheet: Binding) where Content == EmptyView { + self._selectedProductId = selectedProductId + self._showProductInfoSheet = showProductInfoSheet + self.productDetails = nil + } /// Creates the body of the view. + /// public var body: some View { SKHelperSheetBarView(showSheet: $showProductInfoSheet, title: product?.displayName ?? "Product Info") ScrollView { - productDetails(selectedProductId) + productDetails?(selectedProductId) ProductView(id: selectedProductId) { Image(selectedProductId) diff --git a/Sources/SKHelper/Views/SKHelperStoreView.swift b/Sources/SKHelper/Views/SKHelperStoreView.swift index 45131103e..55a163d22 100644 --- a/Sources/SKHelper/Views/SKHelperStoreView.swift +++ b/Sources/SKHelper/Views/SKHelperStoreView.swift @@ -12,6 +12,7 @@ import StoreKit @available(iOS 17.0, macOS 14.6, *) public struct SKHelperStoreView: View { + /// A closure that is passed a `ProductId` and returns a `View` providing product details. public typealias ProductDetailsClosure = (ProductId) -> Content /// The `SKHelper` object. @@ -24,14 +25,14 @@ public struct SKHelperStoreView: View { @State private var productSelected = false /// A closure which is called to display product details. - private var productDetails: ProductDetailsClosure + private var productDetails: ProductDetailsClosure? - /// Creates an `SKHelperStoreView`. When you instantiate this view provide a closure that may be called to display product information. This information will be displayed when the - /// user taps on the product's image. + /// Creates an `SKHelperStoreView`. When you instantiate this view provide a closure that may be called to display product information. + /// This information will be displayed when the user taps on the product's image. /// /// The `ProductId` of the product to display information for is provided to the closure. /// - /// ## Example 1 ## + /// ## Example ## /// /// ``` /// SKHelperStoreView() { productId in @@ -45,17 +46,14 @@ public struct SKHelperStoreView: View { /// .padding() /// } /// ``` - /// - /// Note that you can also provide an empty closure if you do not wish to provide custom content. - /// - /// ## Example 2 ## - /// - /// ``` - /// SKHelperStoreView() { _ in } - /// ``` public init(@ViewBuilder productDetails: @escaping ProductDetailsClosure) { self.productDetails = productDetails } + + /// Creates an `SKHelperStoreView`. Default product information will be displayed when the user taps on the product's image. + public init() where Content == EmptyView { + self.productDetails = nil + } /// Creates the body of the view. public var body: some View { @@ -81,7 +79,8 @@ public struct SKHelperStoreView: View { #endif .storeButton(.hidden, for: .cancellation) // Hides the close "X" at the top-right of the view .sheet(isPresented: $productSelected) { - SKHelperProductView(selectedProductId: $selectedProductId, showProductInfoSheet: $productSelected, productDetails: productDetails) + if let productDetails { SKHelperProductView(selectedProductId: $selectedProductId, showProductInfoSheet: $productSelected, productDetails: productDetails) } + else { SKHelperProductView(selectedProductId: $selectedProductId, showProductInfoSheet: $productSelected) } } } else { @@ -96,8 +95,5 @@ public struct SKHelperStoreView: View { } #Preview { - SKHelperStoreView() { selectedProductId in - Text("Product details for \(selectedProductId)") - } - .environment(SKHelper()) + SKHelperStoreView().environment(SKHelper()) } diff --git a/Sources/SKHelper/Views/SKHelperSubscriptionStoreView.swift b/Sources/SKHelper/Views/SKHelperSubscriptionStoreView.swift index 88755240c..fdd24d0b6 100644 --- a/Sources/SKHelper/Views/SKHelperSubscriptionStoreView.swift +++ b/Sources/SKHelper/Views/SKHelperSubscriptionStoreView.swift @@ -10,36 +10,81 @@ import StoreKit /// Uses the StoreKit `SubscriptionStoreView` to create a list of all avaliable subscriptions. @available(iOS 17.0, macOS 14.6, *) -public struct SKHelperSubscriptionStoreView: View { +public struct SKHelperSubscriptionStoreView: View { + + /// A closure that returns a `View` providing an overview of a group of subscriptions. + public typealias SubscriptionHeaderClosure = () -> Header + + /// A closure that is passed a `ProductId` and returns a `View` providing a subscription image and other details. + public typealias SubscriptionControlClosure = (ProductId) -> Control + + /// A closure that is passed a `ProductId` and returns a `View` providing subscription details. + public typealias SubscriptionDetailsClosure = (ProductId) -> Details /// The `SKHelper` object. @Environment(SKHelper.self) private var store + /// The `ProductId` of the currently selected subscription. + @State private var selectedProductId = "" + + /// true if a subscription has been selected. + @State private var subscriptionSelected = false + + @State private var subscriptionGroupName: String + + /// A closure which is called to display a view forming an overall header for the collection of subscriptions. + private var subscriptionHeader: SubscriptionHeaderClosure? + + /// A closure which is called to display a view which provides a brief overview of each subscription. + private var subscriptionControl: SubscriptionControlClosure? + + /// A closure which is called to display subscription details when the user taps the subscription's image. + private var subscriptionDetails: SubscriptionDetailsClosure? + /// Creates a `SKHelperSubscriptionStoreView`. - public init() {} + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionHeader: @escaping SubscriptionHeaderClosure, + @ViewBuilder subscriptionControl: @escaping SubscriptionControlClosure, + @ViewBuilder subscriptionDetails: @escaping SubscriptionDetailsClosure) { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = subscriptionHeader + self.subscriptionControl = subscriptionControl + self.subscriptionDetails = subscriptionDetails + } /// Creates the body of the view. public var body: some View { if store.hasSubscriptionProducts { - SubscriptionStoreView(subscriptions: store.allSubscriptionProductsByLevel(for: "vip")) { - Image(systemName: "cart.fill") - .resizable() - .frame(maxWidth: 85, maxHeight: 85) - .scaledToFit() - } - .subscriptionStoreButtonLabel(.action) - .subscriptionStoreControlStyle(.prominentPicker) - .storeButton(.hidden, for: .cancellation) // Hides the close "X" at the top-right of the view - .storeButton(.visible, for: .restorePurchases, .policies) - .subscriptionStorePolicyDestination(url: URL(string: "https://russell-archer.github.io/Writerly/privacy/")!, for: .privacyPolicy) - .subscriptionStorePolicyDestination(url: URL(string: "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/")!, for: .termsOfService) - #if os(iOS) - .storeButton(.visible, for: .redeemCode) - #endif - .subscriptionStoreControlIcon { subscription, info in - Image(subscription.id) - .resizable() - .scaledToFit() + ScrollView { + SubscriptionStoreView(subscriptions: store.allSubscriptionProductsByLevel(for: subscriptionGroupName)) { + VStack { subscriptionHeader?() }.padding() + } + .subscriptionStoreButtonLabel(.action) + .subscriptionStoreControlStyle(.prominentPicker) + #if os(iOS) + .storeButton(.visible, for: .redeemCode) + #endif + .storeButton(.visible, for: .restorePurchases, .policies) + .storeButton(.hidden, for: .cancellation) // Hides the close "X" at the top-right of the view + .subscriptionStorePolicyDestination(url: URL(string: "https://russell-archer.github.io/Writerly/privacy/")!, for: .privacyPolicy) + .subscriptionStorePolicyDestination(url: URL(string: "https://www.apple.com/legal/internet-services/itunes/dev/stdeula/")!, for: .termsOfService) + .subscriptionStoreControlIcon { subscription, info in + + subscriptionControl?(subscription.id) + .SKHelperOnTapGesture { + selectedProductId = subscription.id + subscriptionSelected.toggle() + } + } + .sheet(isPresented: $subscriptionSelected) { + if let subscriptionDetails { + SKHelperProductView(selectedProductId: $selectedProductId, showProductInfoSheet: $subscriptionSelected, productDetails: subscriptionDetails) + } else { + SKHelperProductView(selectedProductId: $selectedProductId, showProductInfoSheet: $subscriptionSelected) + } + } } } else { @@ -53,6 +98,99 @@ public struct SKHelperSubscriptionStoreView: View { } } +// MARK: - Additional initializers + +extension SKHelperSubscriptionStoreView { + + /// Creates a `SKHelperSubscriptionStoreView`. + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionHeader: @escaping SubscriptionHeaderClosure) where Control == EmptyView, Details == EmptyView { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = subscriptionHeader + self.subscriptionControl = nil + self.subscriptionDetails = nil + } + + /// Creates a `SKHelperSubscriptionStoreView`. + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionControl: @escaping SubscriptionControlClosure) where Header == EmptyView, Details == EmptyView { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = nil + self.subscriptionControl = subscriptionControl + self.subscriptionDetails = nil + } + + /// Creates a `SKHelperSubscriptionStoreView`. + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionDetails: @escaping SubscriptionDetailsClosure) where Header == EmptyView, Control == EmptyView { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = nil + self.subscriptionControl = nil + self.subscriptionDetails = subscriptionDetails + } + + /// Creates a `SKHelperSubscriptionStoreView`. + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionHeader: @escaping SubscriptionHeaderClosure, + @ViewBuilder subscriptionControl: @escaping SubscriptionControlClosure) where Details == EmptyView { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = subscriptionHeader + self.subscriptionControl = subscriptionControl + self.subscriptionDetails = nil + } + + /// Creates a `SKHelperSubscriptionStoreView`. + public init( + subscriptionGroupName: String, + @ViewBuilder subscriptionHeader: @escaping SubscriptionHeaderClosure, + @ViewBuilder subscriptionDetails: @escaping SubscriptionDetailsClosure) where Control == EmptyView { + + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = subscriptionHeader + self.subscriptionControl = nil + self.subscriptionDetails = subscriptionDetails + } + + /// Creates a `SKHelperSubscriptionStoreView`. + public init(subscriptionGroupName: String) where Header == EmptyView, Control == EmptyView, Details == EmptyView { + self.subscriptionGroupName = subscriptionGroupName + self.subscriptionHeader = nil + self.subscriptionControl = nil + self.subscriptionDetails = nil + } +} + #Preview { - SKHelperSubscriptionStoreView().environment(SKHelper()) + + SKHelperSubscriptionStoreView( + subscriptionGroupName: "vip", + subscriptionHeader: { + VStack { + Image("plant-services").resizable().scaledToFit() + Text("Our top-rated serices will make your plants happy").font(.headline) + } + }, + subscriptionControl: { productId in + + VStack { + Image(productId).resizable().scaledToFit() + Text("A florist will call \(productId == "com.rarcher.subscription.vip.gold" ? "weekly" : "monthly") to water your plants.").font(.caption2) + } + }, + subscriptionDetails: { productId in + + VStack { + Image(productId + ".info").resizable().scaledToFit() + Text("Here is some text about why you might want to buy this product.") + } + }) + .environment(SKHelper()) }