Hi there,
I have checked that my offerings are not nil & I can fetch all other data so far, but the subscriptionPeriod is throwing nil every time & I am stuck at what else to do.
import SwiftUI
import RevenueCat
struct PaywallView: View {
@Environment(\.dismiss) private var dismiss
@State var currentOffering: Offering?
var body: some View {
NavigationView {
VStack {
// Header
VStack {
Text("🌟 My Yarn Pal Pro 🌟")
.padding(.top)
// Close Button
HStack {
Button {
dismiss()
} label: {
Image(systemName: "multiply")
}
.padding(.horizontal)
Spacer()
}
}
.ignoresSafeArea()
.frame(height: 90)
.frame(maxWidth: .infinity)
.background(.mintLight)
.padding(.top, -50)
Spacer()
Text("🧶")
Text("Unlock all the features below to enjoy the full experience of My Yarn Pro")
.multilineTextAlignment(.center)
.padding(.horizontal, 50)
.font(.formsAndSettingsText)
Spacer()
// Features
Grid(alignment: .leading, verticalSpacing: 10) {
GridRow {
Text("Features")
.gridCellColumns(2)
Text("Free")
Text("Pro")
}
.font(.formsAndSettingsText)
.foregroundStyle(.darkGreyText)
Divider()
GridRow {
Text("Single Project Allowance")
.gridCellColumns(2)
Text("3")
.gridColumnAlignment(.center)
Text("Unlimited")
.gridColumnAlignment(.center)
}
Divider()
GridRow {
Text("Multi Project Parts Allowance")
.gridCellColumns(2)
Text("1")
Text("Unlimited")
}
Divider()
GridRow {
Text("How Many Counters on a Project")
.gridCellColumns(2)
Text("1")
Text("2")
}
Divider()
GridRow {
Text("Yarn Stash Limits")
.gridCellColumns(2)
Text("10")
Text("Unlimited")
}
Divider()
GridRow {
Text("More features to come")
.gridCellColumns(2)
}
.font(.formsAndSettingsText)
.foregroundStyle(.darkGreyText)
// TODO: Add background & Update fonts
// TODO: Set spacing directly so each colunm is correct
}
.padding()
.background(.mintLight)
.frame(width: 370)
.font(.notesText)
Spacer()
Text("Ready to go Pro?")
.font(.notePad)
// Purchase
VStack(spacing: 30) {
if currentOffering != nil {
ForEach(currentOffering!.availablePackages) { pkg in
Button {
// TODO: connect button to paywall
} label: {
ZStack {
RoundedRectangle(cornerRadius: 8)
.stroke(style: StrokeStyle())
.frame(width: 280, height: 72)
RoundedRectangle(cornerRadius: 8)
.fill(.mintLight)
.frame(width: 270, height: 62)
VStack(alignment: .center) {
Text("My Yarn Pal Pro")
.font(.notePad)
.bold()
.padding(.bottom, 5)
Text("\(pkg.storeProduct.localizedTitle)")
Text("\(pkg.storeProduct.subscriptionPeriod!.periodTitle) \(pkg.storeProduct.localizedPriceString)")
}
.font(.projectBarDetails)
.foregroundStyle(.darkGreyText)
}
}
}
}
}
.padding(.vertical)
/// Footer
Text("No commitment, cancel anytime.")
.font(.projectBarTitle)
.padding(.bottom, 10)
// TODO: Add correct links
HStack {
Link("Privacy Policy", destination: URL(string: "www.google.com")!)
Rectangle()
.fill(.white)
.frame(width: 1, height: 12)
Link("Terms & Conditions", destination: URL(string: "www.google.com")!)
Rectangle()
.fill(.white)
.frame(width: 1, height: 12)
Button("Restore") {
// TODO: Restore Purchases
}
}
.font(.projectBarTitle)
//TODO: Restore purchases option in settings
}
.padding(.top, 30)
.foregroundStyle(.white)
// .navigationBarTitleDisplayMode(.inline)
//.navigationTitle("🌟 My Yarn Pal Pro 🌟")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.mintDark)
}
// Fetch paywall options
.onAppear {
Purchases.shared.getOfferings { offereings, error in
// If no errors & able to fetch offerings from Revenue Cat
if let offer = offereings?.current, error == nil {
currentOffering = offer
}
}
}
}
}
#Preview {
PaywallView()
}
extension SubscriptionPeriod {
var durationTitle: String {
switch self.unit {
case .day: return "day"
case .week: return "week"
case .month: return "month"
case .year: return "year"
@unknown default: return "Unknown"
}
}
var periodTitle: String {
let periodString = "\(self.value) \(self.durationTitle)"
let pluralized = self.value > 1 ? periodString + "s" : periodString
return pluralized
}
}