diff --git a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj index 168e35455a..26af1d3501 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj +++ b/Tests/TestingApps/SimpleApp/SimpleApp.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 4F4EE7CD2A572F5400D7EAE1 /* SimpleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC046BC2A572E3700A28BCF /* SimpleApp.swift */; }; 4F4EE7CF2A572F5D00D7EAE1 /* DebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC046C32A572E3700A28BCF /* DebugView.swift */; }; 4F4EE7D22A5731E800D7EAE1 /* RevenueCat in Frameworks */ = {isa = PBXBuildFile; productRef = 4F4EE7D12A5731E800D7EAE1 /* RevenueCat */; }; - 4F6E5A502A660DD500C573C7 /* SampleChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F6E5A4F2A660DD500C573C7 /* SampleChart.swift */; }; + 4F71CDD22A992292001B9BEF /* CustomPaywallContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F71CDD12A992292001B9BEF /* CustomPaywallContent.swift */; }; 4FC6F8B22A7403D3002139B2 /* OfferingsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FC6F8B12A7403D3002139B2 /* OfferingsList.swift */; }; 4FC80B202A5DE8E300E95DB0 /* RevenueCatUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4FC80B1F2A5DE8E300E95DB0 /* RevenueCatUI */; }; 4FCA01FB2A3A1CBD00B262C0 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FCA01FA2A3A1CBD00B262C0 /* StoreKit.framework */; }; @@ -46,7 +46,7 @@ 4F4557E52A6FFE6D00160521 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 4F4EE7D02A5731CF00D7EAE1 /* purchases-ios */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "purchases-ios"; path = ../../..; sourceTree = ""; }; 4F6BED9A2A26A64200CD9322 /* SimpleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4F6E5A4F2A660DD500C573C7 /* SampleChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleChart.swift; sourceTree = ""; }; + 4F71CDD12A992292001B9BEF /* CustomPaywallContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPaywallContent.swift; sourceTree = ""; }; 4FC046BC2A572E3700A28BCF /* SimpleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleApp.swift; sourceTree = ""; }; 4FC046BD2A572E3700A28BCF /* SimpleApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SimpleApp.entitlements; sourceTree = ""; }; 4FC046BE2A572E3700A28BCF /* Products.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Products.storekit; sourceTree = ""; }; @@ -80,9 +80,9 @@ 4F34FF622A60AD9A00AADF11 /* AppContentView.swift */, 4FC046C32A572E3700A28BCF /* DebugView.swift */, 4FC6F8B12A7403D3002139B2 /* OfferingsList.swift */, - 4F6E5A4F2A660DD500C573C7 /* SampleChart.swift */, 4FDF111F2A7270F3004F3680 /* SamplePaywallsList.swift */, 4F102E262A840ECC0059EED6 /* CustomPaywall.swift */, + 4F71CDD12A992292001B9BEF /* CustomPaywallContent.swift */, ); path = Views; sourceTree = ""; @@ -225,8 +225,8 @@ buildActionMask = 2147483647; files = ( 4F4EE7CD2A572F5400D7EAE1 /* SimpleApp.swift in Sources */, + 4F71CDD22A992292001B9BEF /* CustomPaywallContent.swift in Sources */, 4F4EE7CF2A572F5D00D7EAE1 /* DebugView.swift in Sources */, - 4F6E5A502A660DD500C573C7 /* SampleChart.swift in Sources */, 4FDF11222A72714C004F3680 /* SamplePaywalls.swift in Sources */, 4F34FF652A60ADBD00AADF11 /* Configuration.swift in Sources */, 4FC6F8B22A7403D3002139B2 /* OfferingsList.swift in Sources */, diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Contents.json b/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Contents.json new file mode 100644 index 0000000000..62beec5b44 --- /dev/null +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Image.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Image.png b/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Image.png new file mode 100644 index 0000000000..e3a0d8c1c4 Binary files /dev/null and b/Tests/TestingApps/SimpleApp/SimpleApp/Assets.xcassets/cat-picture.imageset/Image.png differ diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/AppContentView.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/AppContentView.swift index 722cd5fa5c..e98456901b 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp/Views/AppContentView.swift +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Views/AppContentView.swift @@ -84,9 +84,6 @@ struct AppContentView: View { Spacer() - BarChartView(data: (0..<10).map { _ in Double.random(in: 0..<100)}) - .frame(maxWidth: .infinity) - if let date = info.latestExpirationDate { Text(verbatim: "Your subscription expires: \(date.formatted())") .font(.caption) @@ -96,6 +93,7 @@ struct AppContentView: View { } } .padding(.horizontal) + .frame(maxWidth: .infinity, maxHeight: .infinity) .navigationTitle("Simple App") .task { for await info in self.customerInfoStream { diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift index 99f4008d8c..69aab88f64 100644 --- a/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywall.swift @@ -19,21 +19,12 @@ struct CustomPaywall: View { var body: some View { self.content - .navigationTitle("Custom paywall") } private var content: some View { - VStack { - VStack { - ForEach(Self.colors, id: \.self) { color in - BarChartView( - data: (0..<10).map { _ in Double.random(in: 0..<100)}, - color: color - ) - } - } - .frame(maxWidth: .infinity) + CustomPaywallContent() .scrollableIfNecessary(.vertical) + .background(CustomPaywallContent.backgroundColor) .paywallFooter(offering: self.offering, customerInfo: self.customerInfo, condensed: self.condensed, @@ -41,23 +32,8 @@ struct CustomPaywall: View { introEligibility: self.introEligibility ?? .default(), purchaseHandler: self.purchaseHandler ?? .default() ) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background( - Color(white: 0.8) - .edgesIgnoringSafeArea(.all) - ) } - private static let colors: [Color] = [ - .red, - .green, - .blue, - .indigo, - .mint, - .teal - ].shuffled() - } diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywallContent.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywallContent.swift new file mode 100644 index 0000000000..f08b9135b1 --- /dev/null +++ b/Tests/TestingApps/SimpleApp/SimpleApp/Views/CustomPaywallContent.swift @@ -0,0 +1,161 @@ +// +// CustomPaywallContent.swift +// SimpleApp +// +// Created by Nacho Soto on 8/25/23. +// + + +import SwiftUI + +struct CustomPaywallContent: View { + + @State private var rotation: Double = 0.0 + @State private var starOpacity: Double = 1 + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Image("cat-picture") + .resizable() + .aspectRatio(contentMode: .fit) + .mask { + LinearGradient(colors: [.clear, .white, .white, .white, .clear], + startPoint: .top, + endPoint: .bottom) + } + .padding(.top, -60) + .padding(.bottom, -80) + + HStack(spacing: 0) { + Text("Pawwall") + .font(.system(.largeTitle, design: .rounded).bold()) + .foregroundColor(Marketing.color5) + .padding(.leading) + + Text("Pro") + .font(.system(.largeTitle, design: .rounded).bold()) + + Spacer(minLength: 0) + + Image(systemName: "star.fill") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 30) + .rotationEffect(Angle(degrees: self.rotation)) + .opacity(self.starOpacity) + .padding(.trailing, 24) + .onAppear { + withAnimation(.linear(duration: 30) + .repeatForever(autoreverses: false)) { + self.rotation = 360.0 + } + withAnimation(.easeInOut(duration: 10).repeatForever(autoreverses: true)) { + self.starOpacity = 0.75 + } + } + } + .shadow(radius: 10) + + Text("Get your paws on all the great premium features of Pawall Pro! 😻") + .padding(.horizontal) + + Text("Premium features") + .font(.headline) + .padding(.top, 16) + .padding(.horizontal) + ScrollView(.horizontal, showsIndicators: false) { + HStack { + self.feature(icon: "dollarsign", + name: "Make more money!") + self.feature(icon: "figure.run", + name: "Do it fast!") + self.feature(icon: "bolt.fill", + name: "Zap the competition") + self.feature(icon: "square.fill", + name: "WALLS!") + } + .padding(.horizontal) + } + + Text("What others are saying") + .font(.headline) + .padding(.top, 16) + .padding(.horizontal) + + self.testimonial(name: "Garfield", + review: "Five stars for this paywall builder app, because let's face it, lasagna isn't free and neither are my naps – it helps me monetize both with style!") + self.testimonial(name: "Tom", + review: "This paywall builder app is a game-changer in my pursuit of Jerry. With its cunning customization, I'm closer than ever to catching that elusive mouse!") + self.testimonial(name: "Tony", + review: "It's grrreat for locking up premium content behind paywalls. A real frosted flakes of a deal!") + } + .foregroundColor(.white) + } + + private func feature(icon: String, name: String) -> some View { + VStack(alignment: .center) { + Image(systemName: icon) + .resizable() + .aspectRatio(contentMode: .fit) + .foregroundColor(.white) + .frame(height: 40) + Spacer(minLength: 0) + Text(name) + .font(.subheadline) + .bold() + .multilineTextAlignment(.center) + } + .padding(2) + .frame(width: 90, height: 90) + .padding() + .background(LinearGradient(colors: [Marketing.color2, Marketing.color1], + startPoint: .topLeading, + endPoint: .bottomTrailing)) + .foregroundColor(Marketing.color5) + .mask { + RoundedRectangle(cornerSize: .init(width: 10, height: 10), style: .continuous) + } + } + + private func testimonial(name: String, review: String) -> some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text("⭐️⭐️⭐️⭐️⭐️") + .font(.callout) + + Spacer(minLength: 8) + + Text(name) + .bold() + } + + Text(review) + .font(.callout) + + Spacer(minLength: 0) + } + .frame(height: 119) + .padding() + .background(LinearGradient(colors: [Marketing.color5, Marketing.color4.opacity(0.8)], + startPoint: .topLeading, endPoint: .bottomTrailing)) + .foregroundColor(Marketing.color1) + .mask { + RoundedRectangle(cornerSize: .init(width: 10, height: 10), style: .continuous) + } + .padding(.horizontal) + } + + static let backgroundColor = Marketing.color3 + +} + +private enum Marketing { + + static let color1 = Color(red: 0.004, green: 0.067, blue: 0.149) + static let color2 = Color(red: 0.039, green: 0.231, blue: 0.349) + static let color3 = Color(red: 0.318, green: 0.463, blue: 0.549) + static let color4 = Color(red: 0.584, green: 0.686, blue: 0.749) + static let color5 = Color(red: 0.69, green: 0.804, blue: 0.851) + +} + diff --git a/Tests/TestingApps/SimpleApp/SimpleApp/Views/SampleChart.swift b/Tests/TestingApps/SimpleApp/SimpleApp/Views/SampleChart.swift deleted file mode 100644 index 5abef03af0..0000000000 --- a/Tests/TestingApps/SimpleApp/SimpleApp/Views/SampleChart.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// SampleChart.swift -// SimpleApp -// -// Created by Nacho Soto on 7/17/23. -// - -import SwiftUI - -struct BarChartView: View { - - let data: [Double] - let color: Color - - private let maxValue: Double - - init(data: [Double], color: Color = .blue) { - self.data = data - self.color = color - self.maxValue = data.max() ?? 0 - } - - var body: some View { - HStack(alignment: .bottom) { - ForEach(self.data, id: \.self) { value in - BarView(value: value, - maxValue: self.maxValue, - color: self.color) - } - } - } - -} - -private struct BarView: View { - - let value: Double - let maxValue: Double - let color: Color - - var body: some View { - VStack { - Rectangle() - .fill(self.color) - .frame(width: 30, height: CGFloat(self.value / self.maxValue) * 120) - - Text(String(Int(self.value))) - .font(.caption) - } - } - -} - -struct ContentView_Previews: PreviewProvider { - - static var previews: some View { - BarChartView(data: [12, 6, 10, 8, 15, 9]) - } - -}