From 291b4376c27c7d6a6f07c5d5ecf0a066731f0303 Mon Sep 17 00:00:00 2001 From: Geoff Pado Date: Mon, 2 Dec 2024 13:10:43 -0800 Subject: [PATCH 1/3] Use new purchase marketing footer --- .../Resources/en.lproj/Localizable.strings | 6 ++ .../Footer/PurchaseMarketingFooter.swift | 14 ++++ .../PurchaseMarketingFooterContents.swift | 15 ++++ .../Footer/PurchaseMarketingFooterLink.swift | 21 +++++ .../PurchaseMarketingFooterLinkSection.swift | 76 +++++++++++++++++++ ...PurchaseMarketingFooterLinkSeparator.swift | 14 ++++ .../PurchaseMarketingFooterPrivacyLink.swift | 26 +++++++ ...urchaseMarketingFooterPurchaseButton.swift | 70 +++++++++++++++++ .../PurchaseMarketingFooterRestoreLink.swift | 27 +++++++ .../PurchaseMarketingHostingController.swift | 1 + .../Sources/PurchaseMarketingView.swift | 19 +++-- .../PurchaseMarketingTopBarCompact.swift | 5 -- .../PurchaseMarketingTopBarRegular.swift | 59 +------------- 13 files changed, 286 insertions(+), 67 deletions(-) create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooter.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterContents.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSection.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSeparator.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPrivacyLink.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift create mode 100644 Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterRestoreLink.swift diff --git a/Modules/Capabilities/PurchaseMarketing/Resources/en.lproj/Localizable.strings b/Modules/Capabilities/PurchaseMarketing/Resources/en.lproj/Localizable.strings index 1b83919e..19dce24d 100644 --- a/Modules/Capabilities/PurchaseMarketing/Resources/en.lproj/Localizable.strings +++ b/Modules/Capabilities/PurchaseMarketing/Resources/en.lproj/Localizable.strings @@ -10,6 +10,12 @@ // Text separating the two purchase buttons "PurchaseButtonSeparator.text" = "or"; +"PurchaseMarketingFooterPrivacyLink.shortTitle" = "Privacy"; +"PurchaseMarketingFooterPrivacyLink.title" = "Privacy Policy"; + +"PurchaseMarketingFooterRestoreLink.shortTitle" = "Restore"; +"PurchaseMarketingFooterRestoreLink.title" = "Restore Purchases"; + // Text for the headline in purchase marketing "PurchaseMarketingTopBarHeadlineLabel.text" = "Ultra Highlighter"; diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooter.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooter.swift new file mode 100644 index 00000000..bd6fc72b --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooter.swift @@ -0,0 +1,14 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import ErrorHandling +import Purchasing +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooter: View { + var body: some View { + PurchaseMarketingFooterContents() + .frame(maxWidth: .infinity, minHeight: 140) + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterContents.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterContents.swift new file mode 100644 index 00000000..4621298a --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterContents.swift @@ -0,0 +1,15 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import Purchasing +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterContents: View { + var body: some View { + VStack(spacing: 20) { + PurchaseMarketingFooterPurchaseButton() + PurchaseMarketingFooterLinkSection() + }.padding() + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift new file mode 100644 index 00000000..86a271ce --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift @@ -0,0 +1,21 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterLink: View { + private let title: String + private let action: () -> Void + init(title: String, action: @escaping () -> Void) { + self.title = title + self.action = action + } + + var body: some View { + Button(title, action: action) + .lineLimit(nil) + .font(.footnote) + .tint(.white) + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSection.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSection.swift new file mode 100644 index 00000000..2c2045ec --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSection.swift @@ -0,0 +1,76 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterLinkSection: View { + var body: some View { + ViewThatFits(in: .horizontal) { + // Restore Purchases — Terms & Conditions — Privacy Policy + HStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: false) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) + } + + // Restore — Terms & Conditions — Privacy Policy + HStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) + } + + // Restore — Terms & Conditions — Privacy + HStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) + } + + // Restore — Terms — Privacy + HStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) + } + + // Restore Purchases — Terms & Conditions — Privacy Policy + VStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: false) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) + } + .frame(maxWidth: .infinity) + + // Restore — Terms & Conditions — Privacy Policy + VStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: false) + } + .frame(maxWidth: .infinity) + + // Restore — Terms & Conditions — Privacy + VStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) + } + .frame(maxWidth: .infinity) + + // Restore — Terms — Privacy + VStack { + PurchaseMarketingFooterRestoreLink(usesShortTitle: true) + PurchaseMarketingFooterLinkSeparator() + PurchaseMarketingFooterPrivacyLink(usesShortTitle: true) + } + .frame(maxWidth: .infinity) + } + } +} + +@available(iOS 17.0, *) +#Preview(traits: .sizeThatFitsLayout) { + PurchaseMarketingFooterLinkSection() +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSeparator.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSeparator.swift new file mode 100644 index 00000000..ce232408 --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLinkSeparator.swift @@ -0,0 +1,14 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterLinkSeparator: View { + var body: some View { + Text(verbatim: " — ") + .accessibilityHidden(true) + .font(.footnote) + .foregroundStyle(.white) + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPrivacyLink.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPrivacyLink.swift new file mode 100644 index 00000000..7f0976e4 --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPrivacyLink.swift @@ -0,0 +1,26 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterPrivacyLink: View { + private let usesShortTitle: Bool + init(usesShortTitle: Bool) { + self.usesShortTitle = usesShortTitle + } + + @Environment(\.openURL) private var openURL + private func openPrivacy() { + guard let privacyURL = URL(string: "https://blackhighlighter.app/privacy/") else { return } + openURL(privacyURL) + } + + var body: some View { + if usesShortTitle { + PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterPrivacyLink.shortTitle, action: openPrivacy) + } else { + PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterPrivacyLink.title, action: openPrivacy) + } + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift new file mode 100644 index 00000000..0553ed22 --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift @@ -0,0 +1,70 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import DesignSystem +import ErrorHandling +import Purchasing +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterPurchaseButton: View { + @State private var purchaseState: PurchaseState + + // allWeAskIsThatYouLetUsHaveItYourWay by @AdamWulf on 2024-05-15 + private let allWeAskIsThatYouLetUsHaveItYourWay: any PurchaseRepository + private let errorHandler = ErrorHandler() + init( + purchaseRepository: any PurchaseRepository = Purchasing.repository + ) { + _purchaseState = State(initialValue: purchaseRepository.withCheese) + self.allWeAskIsThatYouLetUsHaveItYourWay = purchaseRepository + } + + var body: some View { + Button { + guard purchaseState.isReadyForPurchase else { return } + purchaseState = .purchasing + Task { + purchaseState = await allWeAskIsThatYouLetUsHaveItYourWay.purchase() + } + } label: { + Text(title) + .fontWeight(.bold) + .foregroundStyle(Color.black) + .padding(12) + .frame(maxWidth: .infinity, minHeight: 44) + .background { + RoundedRectangle(cornerRadius: 8) + .fill(Color.white) + } + } + .disabled(disabled) + .onReceive(allWeAskIsThatYouLetUsHaveItYourWay.purchaseStates.eraseToAnyPublisher()) { newState in + purchaseState = newState + } + } + + private var title: String { + switch purchaseState { + case .loading: + return Strings.loadingTitle + case .purchasing, .restoring: + return Strings.purchasingTitle + case .readyForPurchase(let product): + return Strings.readyTitle(product.displayPrice) + case .unavailable: + return Strings.loadingTitle + case .purchased: + return Strings.purchasedTitle + } + } + + private var disabled: Bool { + switch purchaseState { + case .readyForPurchase: return false + default: return true + } + } + + private typealias Strings = PurchaseMarketingStrings.PurchaseButton +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterRestoreLink.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterRestoreLink.swift new file mode 100644 index 00000000..088e73dd --- /dev/null +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterRestoreLink.swift @@ -0,0 +1,27 @@ +// Created by Geoff Pado on 12/2/24. +// Copyright © 2024 Cocoatype, LLC. All rights reserved. + +import StoreKit +import SwiftUI + +@available(iOS 16.0, *) +struct PurchaseMarketingFooterRestoreLink: View { + private let usesShortTitle: Bool + init(usesShortTitle: Bool) { + self.usesShortTitle = usesShortTitle + } + + private func restore() { + Task { + try await AppStore.sync() + } + } + + var body: some View { + if usesShortTitle { + PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterRestoreLink.shortTitle, action: restore) + } else { + PurchaseMarketingFooterLink(title: PurchaseMarketingStrings.PurchaseMarketingFooterRestoreLink.title, action: restore) + } + } +} diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingHostingController.swift b/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingHostingController.swift index a19b08dd..580d0ddc 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingHostingController.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingHostingController.swift @@ -4,6 +4,7 @@ import SwiftUI import UIKit +@available(iOS 16.0, *) public class PurchaseMarketingHostingController: UIHostingController { public init() { super.init(rootView: PurchaseMarketingView()) diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingView.swift b/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingView.swift index 155feab2..4b701217 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingView.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/PurchaseMarketingView.swift @@ -4,6 +4,7 @@ import DesignSystem import SwiftUI +@available(iOS 16.0, *) public struct PurchaseMarketingView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass @@ -50,6 +51,9 @@ public struct PurchaseMarketingView: View { } .fill() .navigationBarHidden(true) + }.safeAreaInset(edge: .bottom) { + PurchaseMarketingFooter() + .background(Color.appPrimary, ignoresSafeAreaEdges: .bottom) } } @@ -75,11 +79,12 @@ public struct PurchaseMarketingView: View { private typealias Strings = PurchaseMarketingStrings.PurchaseMarketingView } -enum PurchaseMarketingView_Previews: PreviewProvider { - static var previews: some View { - PurchaseMarketingView() - .preferredColorScheme(.dark) - .environment(\.readableWidth, 288) -// .previewLayout(.fixed(width: 640, height: 1024)) - } +@available(iOS 16.0, *) +#Preview { + Color.black + .ignoresSafeArea() + .sheet(isPresented: .constant(true)) { + PurchaseMarketingView() + .frame(width: 640) + } } diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarCompact.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarCompact.swift index 0a791763..535d40ae 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarCompact.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarCompact.swift @@ -8,11 +8,6 @@ struct PurchaseMarketingTopBarCompact: View { VStack(alignment: .leading, spacing: 4) { PurchaseMarketingTopBarHeadline() PurchaseMarketingTopBarSubheadline() - if #available(iOS 15, *) { - PurchaseMarketingTopBarButtonStack() - } else { - LegacyPurchaseMarketingTopBarButtonStack() - } } .frame(maxWidth: .infinity, alignment: .leading) .padding(EdgeInsets(top: 40, leading: 20, bottom: 20, trailing: 20)) diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarRegular.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarRegular.swift index b6ecb4e3..d573d089 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarRegular.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Top Bar/PurchaseMarketingTopBarRegular.swift @@ -4,68 +4,17 @@ import SwiftUI struct PurchaseMarketingTopBarRegular: View { - @State private var textWidth: CGFloat? - var body: some View { VStack(alignment: .leading, spacing: 4) { - PurchaseMarketingTopBarHeadline().modifier(SetWidthViewModifier(textWidth: $textWidth)) - PurchaseMarketingTopBarSubheadline().modifier(GetWidthViewModifier(textWidth: $textWidth)) - HStack { - PurchaseButton() - PurchaseButtonSeparator() - PurchaseRestoreButton() - } + PurchaseMarketingTopBarHeadline() + PurchaseMarketingTopBarSubheadline() } .padding(40) .frame(maxWidth: .infinity, alignment: .leading) .background(Color.primaryDark) } - - private struct SetWidthViewModifier: ViewModifier { - @Binding var textWidth: CGFloat? - init(textWidth: Binding) { - _textWidth = textWidth - } - - func body(content: Content) -> some View { - content - .background(GeometryReader { proxy in - Color.clear.preference(key: TextWidthPreferenceKey.self, value: proxy.size.width) - }) - .frame(width: textWidth, alignment: .leading) - .onPreferenceChange(TextWidthPreferenceKey.self) { - textWidth = $0 - } - } - } - - private struct GetWidthViewModifier: ViewModifier { - @Binding var textWidth: CGFloat? - init(textWidth: Binding) { - _textWidth = textWidth - } - - func body(content: Content) -> some View { - content - .frame(width: textWidth, alignment: .leading) - .fixedSize(horizontal: false, vertical: true) - .onPreferenceChange(TextWidthPreferenceKey.self) { - textWidth = $0 - } - } - } - - private struct TextWidthPreferenceKey: PreferenceKey { - static let defaultValue: CGFloat = .zero - - static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { - value = min(value, nextValue()) - } - } } -enum PurchaseMarketingTopBarPreviews: PreviewProvider { - static var previews: some View { - PurchaseMarketingTopBarRegular().previewLayout(.sizeThatFits) - } +#Preview { + PurchaseMarketingTopBarRegular() } From 8b3646cb330eec8b3acccfc5e1688d5f65f82ed0 Mon Sep 17 00:00:00 2001 From: Geoff Pado Date: Mon, 2 Dec 2024 13:40:39 -0800 Subject: [PATCH 2/3] Finish updating paywall in rest of app --- .../Sources/Footer/PurchaseMarketingFooterLink.swift | 1 + .../Footer/PurchaseMarketingFooterPurchaseButton.swift | 1 + .../SettingsUI/Sources/Desktop/DesktopSettingsView.swift | 2 +- .../Content/SettingsContentPurchasedFeaturesSection.swift | 2 +- .../Legacy/Core/Sources/Application/AppViewController.swift | 4 +++- .../Sources/Editing View/PhotoEditingViewController.swift | 4 +++- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift index 86a271ce..b24a95e6 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterLink.swift @@ -14,6 +14,7 @@ struct PurchaseMarketingFooterLink: View { var body: some View { Button(title, action: action) + .buttonStyle(.plain) .lineLimit(nil) .font(.footnote) .tint(.white) diff --git a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift index 0553ed22..0ca3b7df 100644 --- a/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift +++ b/Modules/Capabilities/PurchaseMarketing/Sources/Footer/PurchaseMarketingFooterPurchaseButton.swift @@ -38,6 +38,7 @@ struct PurchaseMarketingFooterPurchaseButton: View { .fill(Color.white) } } + .buttonStyle(.plain) .disabled(disabled) .onReceive(allWeAskIsThatYouLetUsHaveItYourWay.purchaseStates.eraseToAnyPublisher()) { newState in purchaseState = newState diff --git a/Modules/Capabilities/SettingsUI/Sources/Desktop/DesktopSettingsView.swift b/Modules/Capabilities/SettingsUI/Sources/Desktop/DesktopSettingsView.swift index f3955c1a..179e5a2c 100644 --- a/Modules/Capabilities/SettingsUI/Sources/Desktop/DesktopSettingsView.swift +++ b/Modules/Capabilities/SettingsUI/Sources/Desktop/DesktopSettingsView.swift @@ -23,7 +23,7 @@ public struct DesktopSettingsView: View { Group { if purchaseState == .purchased { DesktopAutoRedactionsListViewControllerRepresentable() - } else { + } else if #available(iOS 16.0, *) { PurchaseMarketingView() } } diff --git a/Modules/Capabilities/SettingsUI/Sources/List/Content/SettingsContentPurchasedFeaturesSection.swift b/Modules/Capabilities/SettingsUI/Sources/List/Content/SettingsContentPurchasedFeaturesSection.swift index 24c1fe9c..278d3c56 100644 --- a/Modules/Capabilities/SettingsUI/Sources/List/Content/SettingsContentPurchasedFeaturesSection.swift +++ b/Modules/Capabilities/SettingsUI/Sources/List/Content/SettingsContentPurchasedFeaturesSection.swift @@ -17,7 +17,7 @@ struct SettingsContentPurchasedFeaturesSection: View { var body: some View { Section { - if purchaseState != .purchased { + if #available(iOS 16.0, *), purchaseState != .purchased { PurchaseNavigationLink(destination: PurchaseMarketingView()) } diff --git a/Modules/Legacy/Core/Sources/Application/AppViewController.swift b/Modules/Legacy/Core/Sources/Application/AppViewController.swift index f49621e8..996387ba 100644 --- a/Modules/Legacy/Core/Sources/Application/AppViewController.swift +++ b/Modules/Legacy/Core/Sources/Application/AppViewController.swift @@ -86,7 +86,9 @@ class AppViewController: UIViewController, PhotoEditorPresenting, DocumentScanni // MARK: Settings View Controller @objc func presentPurchaseMarketing() { - present(PurchaseMarketingHostingController(), animated: true) + if #available(iOS 16.0, *) { + present(PurchaseMarketingHostingController(), animated: true) + } } @objc func presentSettingsViewController() { diff --git a/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift b/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift index 786cde68..2964c639 100644 --- a/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift +++ b/Modules/Legacy/Editing/Sources/Editing View/PhotoEditingViewController.swift @@ -326,7 +326,9 @@ public class PhotoEditingViewController: UIViewController, UIScrollViewDelegate, present( PhotoEditingAutoRedactionsAccessProvider() .autoRedactionsAccessViewController { [weak self] in - self?.present(PurchaseMarketingHostingController(), animated: true) + if #available(iOS 16, *) { + self?.present(PurchaseMarketingHostingController(), animated: true) + } }, animated: true ) From ae651969af15fba5efb53d71151a24cefa2fb7c8 Mon Sep 17 00:00:00 2001 From: Geoff Pado Date: Mon, 2 Dec 2024 13:42:57 -0800 Subject: [PATCH 3/3] Fix test compilation --- .../Content/SettingsContentPurchasedFeaturesSectionTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Capabilities/SettingsUI/Tests/List/Content/SettingsContentPurchasedFeaturesSectionTests.swift b/Modules/Capabilities/SettingsUI/Tests/List/Content/SettingsContentPurchasedFeaturesSectionTests.swift index 29c539fb..bb4f3cb0 100644 --- a/Modules/Capabilities/SettingsUI/Tests/List/Content/SettingsContentPurchasedFeaturesSectionTests.swift +++ b/Modules/Capabilities/SettingsUI/Tests/List/Content/SettingsContentPurchasedFeaturesSectionTests.swift @@ -8,6 +8,7 @@ import XCTest @testable import SettingsUI +@available(iOS 16.0, *) class SettingsContentPurchasedFeaturesSectionTests: XCTestCase { func testContainsPurchaseNavigationLinkIfNotPurchased() throws { let section = try SettingsContentPurchasedFeaturesSection(state: .loading).inspect().find(SettingsContentPurchasedFeaturesSection.self)