Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

Commit e42d2d3

Browse files
committed
fix: resolve build errors in AllProductsView
- Fix displayName optional handling - Fix description optional binding - Fix subscriptionCard function scope
1 parent ce206d0 commit e42d2d3

File tree

3 files changed

+291
-1
lines changed

3 files changed

+291
-1
lines changed

Example/Martie.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
C0E1F5ED2C8F1A9500123456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0E1F5EC2C8F1A9500123456 /* Assets.xcassets */; };
1313
C0E1F5F02C8F1A9500123456 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0E1F5EF2C8F1A9500123456 /* Preview Assets.xcassets */; };
1414
C0E1F5F82C8F1AA300123456 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E1F5F72C8F1AA300123456 /* HomeScreen.swift */; };
15+
C0APV0022D20000000000001 /* AllProductsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0APV0012D20000000000001 /* AllProductsView.swift */; };
1516
C0E1F5FA2C8F1AAB00123456 /* PurchaseFlowScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E1F5F92C8F1AAB00123456 /* PurchaseFlowScreen.swift */; };
1617
C0E1F5FC2C8F1AB000123456 /* SubscriptionFlowScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E1F5FB2C8F1AB000123456 /* SubscriptionFlowScreen.swift */; };
1718
C0E1F5FE2C8F1AB500123456 /* AvailablePurchasesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0E1F5FD2C8F1AB500123456 /* AvailablePurchasesScreen.swift */; };
@@ -48,6 +49,7 @@
4849
C0E1F5FB2C8F1AB000123456 /* SubscriptionFlowScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFlowScreen.swift; sourceTree = "<group>"; };
4950
C0E1F5FD2C8F1AB500123456 /* AvailablePurchasesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailablePurchasesScreen.swift; sourceTree = "<group>"; };
5051
C0E1F5FF2C8F1ABA00123456 /* OfferCodeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfferCodeScreen.swift; sourceTree = "<group>"; };
52+
C0APV0012D20000000000001 /* AllProductsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllProductsView.swift; sourceTree = "<group>"; };
5153
C0E1F6032C8F1AC500123456 /* AppColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppColors.swift; sourceTree = "<group>"; };
5254
C0IAC0002D10000000000001 /* IapCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IapCompat.swift; sourceTree = "<group>"; };
5355
C0UI10012D00000000000001 /* FeatureCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureCard.swift; sourceTree = "<group>"; };
@@ -123,6 +125,7 @@
123125
children = (
124126
C0UI20012D00000000000001 /* uis */,
125127
C0E1F5F72C8F1AA300123456 /* HomeScreen.swift */,
128+
C0APV0012D20000000000001 /* AllProductsView.swift */,
126129
C0E1F5F92C8F1AAB00123456 /* PurchaseFlowScreen.swift */,
127130
C0E1F5FB2C8F1AB000123456 /* SubscriptionFlowScreen.swift */,
128131
C0E1F5FD2C8F1AB500123456 /* AvailablePurchasesScreen.swift */,
@@ -248,6 +251,7 @@
248251
files = (
249252
C0E1F5EB2C8F1A9400123456 /* ContentView.swift in Sources */,
250253
C0E1F5F82C8F1AA300123456 /* HomeScreen.swift in Sources */,
254+
C0APV0022D20000000000001 /* AllProductsView.swift in Sources */,
251255
C0E1F5FA2C8F1AAB00123456 /* PurchaseFlowScreen.swift in Sources */,
252256
C0E1F5FC2C8F1AB000123456 /* SubscriptionFlowScreen.swift in Sources */,
253257
C0E1F5FE2C8F1AB500123456 /* AvailablePurchasesScreen.swift in Sources */,
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
import SwiftUI
2+
import OpenIAP
3+
4+
@available(iOS 15.0, *)
5+
struct AllProductsView: View {
6+
@StateObject private var store = OpenIapStore()
7+
@State private var isLoading = false
8+
@State private var errorMessage: String?
9+
@Environment(\.dismiss) private var dismiss
10+
11+
// Product IDs from other screens
12+
private let allProductIds: [String] = [
13+
"dev.hyo.martie.10bulbs",
14+
"dev.hyo.martie.30bulbs",
15+
"dev.hyo.martie.premium",
16+
"dev.hyo.martie.premium_year"
17+
]
18+
19+
20+
var body: some View {
21+
NavigationView {
22+
ZStack {
23+
Color(UIColor.systemGroupedBackground)
24+
.ignoresSafeArea()
25+
26+
ScrollView {
27+
VStack(spacing: 16) {
28+
if !store.isConnected {
29+
connectionWarningCard
30+
}
31+
32+
if isLoading {
33+
loadingCard
34+
}
35+
36+
if !isLoading && store.iosProducts.isEmpty && store.iosSubscriptionProducts.isEmpty && store.isConnected {
37+
emptyStateCard
38+
}
39+
40+
// Display regular products
41+
ForEach(store.iosProducts, id: \.id) { product in
42+
productCard(for: product)
43+
}
44+
45+
// Display subscription products
46+
ForEach(store.iosSubscriptionProducts, id: \.id) { subscription in
47+
subscriptionCard(for: subscription)
48+
}
49+
50+
if let error = errorMessage {
51+
errorCard(message: error)
52+
}
53+
}
54+
.padding()
55+
}
56+
}
57+
.navigationTitle("All Products")
58+
.navigationBarTitleDisplayMode(.large)
59+
.toolbar {
60+
ToolbarItem(placement: .navigationBarLeading) {
61+
Button(action: {
62+
Task {
63+
try? await store.endConnection()
64+
}
65+
dismiss()
66+
}) {
67+
Image(systemName: "arrow.left")
68+
.foregroundColor(.primary)
69+
}
70+
}
71+
}
72+
}
73+
.navigationViewStyle(StackNavigationViewStyle())
74+
.onAppear {
75+
Task {
76+
await initializeStore()
77+
}
78+
}
79+
}
80+
81+
private var connectionWarningCard: some View {
82+
HStack {
83+
Image(systemName: "exclamationmark.triangle.fill")
84+
.foregroundColor(.orange)
85+
.font(.title3)
86+
87+
VStack(alignment: .leading, spacing: 4) {
88+
Text("Not Connected")
89+
.font(.headline)
90+
Text("Billing service is not connected")
91+
.font(.caption)
92+
.foregroundColor(.secondary)
93+
}
94+
95+
Spacer()
96+
97+
Button("Retry") {
98+
Task {
99+
await initializeStore()
100+
}
101+
}
102+
.buttonStyle(.bordered)
103+
.controlSize(.small)
104+
}
105+
.padding()
106+
.background(Color.orange.opacity(0.1))
107+
.cornerRadius(12)
108+
}
109+
110+
private var loadingCard: some View {
111+
HStack {
112+
ProgressView()
113+
.progressViewStyle(CircularProgressViewStyle())
114+
115+
Text("Loading products...")
116+
.font(.subheadline)
117+
.foregroundColor(.secondary)
118+
.padding(.leading, 8)
119+
}
120+
.frame(maxWidth: .infinity)
121+
.padding()
122+
.background(Color(UIColor.secondarySystemGroupedBackground))
123+
.cornerRadius(12)
124+
}
125+
126+
private var emptyStateCard: some View {
127+
VStack(spacing: 12) {
128+
Image(systemName: "bag")
129+
.font(.largeTitle)
130+
.foregroundColor(.secondary)
131+
132+
Text("No products available")
133+
.font(.headline)
134+
.foregroundColor(.secondary)
135+
}
136+
.frame(maxWidth: .infinity)
137+
.padding(.vertical, 32)
138+
.background(Color(UIColor.secondarySystemGroupedBackground))
139+
.cornerRadius(12)
140+
}
141+
142+
private func productCard(for product: ProductIOS) -> some View {
143+
VStack(alignment: .leading, spacing: 12) {
144+
HStack(alignment: .top) {
145+
VStack(alignment: .leading, spacing: 4) {
146+
Text(product.displayName ?? product.displayNameIOS)
147+
.font(.headline)
148+
149+
if !product.description.isEmpty {
150+
Text(product.description)
151+
.font(.caption)
152+
.foregroundColor(.secondary)
153+
.lineLimit(2)
154+
}
155+
}
156+
157+
Spacer()
158+
159+
// Product type badge
160+
Text(product.type == .subs ? "subs" : "in-app")
161+
.font(.caption)
162+
.fontWeight(.medium)
163+
.padding(.horizontal, 8)
164+
.padding(.vertical, 4)
165+
.background(product.type == .subs ? Color.blue.opacity(0.1) : Color.green.opacity(0.1))
166+
.foregroundColor(product.type == .subs ? .blue : .green)
167+
.cornerRadius(6)
168+
}
169+
170+
HStack {
171+
Text(product.displayPrice ?? "--")
172+
.font(.title2)
173+
.fontWeight(.bold)
174+
.foregroundColor(.blue)
175+
176+
Spacer()
177+
178+
Text("SKU: \(product.id)")
179+
.font(.caption2)
180+
.foregroundColor(.secondary)
181+
}
182+
}
183+
.padding()
184+
.background(Color(UIColor.secondarySystemGroupedBackground))
185+
.cornerRadius(12)
186+
.shadow(color: .black.opacity(0.05), radius: 2, x: 0, y: 1)
187+
}
188+
189+
private func errorCard(message: String) -> some View {
190+
HStack {
191+
Image(systemName: "exclamationmark.circle.fill")
192+
.foregroundColor(.red)
193+
194+
Text(message)
195+
.font(.subheadline)
196+
.foregroundColor(.red)
197+
198+
Spacer()
199+
}
200+
.padding()
201+
.background(Color.red.opacity(0.1))
202+
.cornerRadius(12)
203+
}
204+
205+
private func initializeStore() async {
206+
isLoading = true
207+
errorMessage = nil
208+
209+
do {
210+
try await store.initConnection()
211+
212+
if store.isConnected {
213+
// Fetch all products using "all" type
214+
try await store.fetchProducts(
215+
skus: allProductIds,
216+
type: .all
217+
)
218+
} else {
219+
errorMessage = "Failed to connect to App Store"
220+
}
221+
} catch {
222+
errorMessage = error.localizedDescription
223+
}
224+
225+
isLoading = false
226+
}
227+
228+
private func subscriptionCard(for subscription: ProductSubscriptionIOS) -> some View {
229+
VStack(alignment: .leading, spacing: 12) {
230+
HStack(alignment: .top) {
231+
VStack(alignment: .leading, spacing: 4) {
232+
Text(subscription.displayName ?? subscription.displayNameIOS)
233+
.font(.headline)
234+
235+
if !subscription.description.isEmpty {
236+
Text(subscription.description)
237+
.font(.caption)
238+
.foregroundColor(.secondary)
239+
.lineLimit(2)
240+
}
241+
}
242+
243+
Spacer()
244+
245+
// Subscription badge
246+
Text("subs")
247+
.font(.caption)
248+
.fontWeight(.medium)
249+
.padding(.horizontal, 8)
250+
.padding(.vertical, 4)
251+
.background(Color.blue.opacity(0.1))
252+
.foregroundColor(.blue)
253+
.cornerRadius(6)
254+
}
255+
256+
HStack {
257+
Text(subscription.displayPrice)
258+
.font(.title2)
259+
.fontWeight(.bold)
260+
.foregroundColor(.blue)
261+
262+
Spacer()
263+
264+
Text("SKU: \(subscription.id)")
265+
.font(.caption2)
266+
.foregroundColor(.secondary)
267+
}
268+
}
269+
.padding()
270+
.background(Color(UIColor.secondarySystemGroupedBackground))
271+
.cornerRadius(12)
272+
.shadow(color: .black.opacity(0.05), radius: 2, x: 0, y: 1)
273+
}
274+
}
275+
276+
#Preview {
277+
AllProductsView()
278+
}

Example/OpenIapExample/Screens/HomeScreen.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,19 @@ struct HomeScreen: View {
4141
.padding(.horizontal)
4242

4343
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 16), count: 2), spacing: 16) {
44+
FeatureCard(
45+
title: "OpenIAP\nExample",
46+
subtitle: "View all products",
47+
icon: "bag.fill",
48+
color: AppColors.primary,
49+
destination: AnyView(AllProductsView())
50+
)
51+
4452
FeatureCard(
4553
title: "Purchase\nFlow",
4654
subtitle: "Test product purchases",
4755
icon: "cart.fill",
48-
color: AppColors.primary,
56+
color: Color.teal,
4957
destination: AnyView(PurchaseFlowScreen())
5058
)
5159

0 commit comments

Comments
 (0)