fix: wrap iOS 17+ APIs in @available checks for iOS 16 compat
ContentUnavailableView and .symbolEffect(.pulse) require iOS 17+ but deployment target is iOS 16. Wrapped all usages in if #available(iOS 17.0, *) with VStack-based fallbacks for iOS 16. Files fixed: - ScanView.swift (4 ContentUnavailableView + 1 symbolEffect) - QRScannerView.swift (1 ContentUnavailableView) - BusinessListView.swift (2 ContentUnavailableView)
This commit is contained in:
parent
fe2ee59930
commit
2496cab7f3
3 changed files with 154 additions and 37 deletions
|
|
@ -14,6 +14,7 @@ struct BusinessListView: View {
|
|||
if isLoading {
|
||||
ProgressView("Loading businesses…")
|
||||
} else if let error = errorMessage {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView {
|
||||
Label("Error", systemImage: "exclamationmark.triangle")
|
||||
} description: {
|
||||
|
|
@ -21,12 +22,43 @@ struct BusinessListView: View {
|
|||
} actions: {
|
||||
Button("Retry") { Task { await loadBusinesses() } }
|
||||
}
|
||||
} else {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.orange)
|
||||
Text("Error")
|
||||
.font(.headline)
|
||||
Text(error)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button("Retry") { Task { await loadBusinesses() } }
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
} else if businesses.isEmpty {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView(
|
||||
"No Businesses",
|
||||
systemImage: "building.2",
|
||||
description: Text("You don't have any businesses yet.")
|
||||
)
|
||||
} else {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "building.2")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.secondary)
|
||||
Text("No Businesses")
|
||||
.font(.headline)
|
||||
Text("You don't have any businesses yet.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.padding()
|
||||
}
|
||||
} else {
|
||||
List(businesses) { business in
|
||||
Button {
|
||||
|
|
|
|||
|
|
@ -141,7 +141,9 @@ struct QRScannerView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var cameraPermissionDenied: some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView {
|
||||
Label("Camera Access Required", systemImage: "camera.fill")
|
||||
} description: {
|
||||
|
|
@ -154,6 +156,26 @@ struct QRScannerView: View {
|
|||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
} else {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "camera.fill")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Camera Access Required")
|
||||
.font(.headline)
|
||||
Text("Open Settings and enable camera access for Payfrit Beacon.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button("Open Settings") {
|
||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
|
|
|||
|
|
@ -129,21 +129,40 @@ struct ScanView: View {
|
|||
.padding()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var selectServicePointPrompt: some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView(
|
||||
"Select a Service Point",
|
||||
systemImage: "mappin.and.ellipse",
|
||||
description: Text("Choose or create a service point (table) to provision a beacon for.")
|
||||
)
|
||||
} else {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Select a Service Point")
|
||||
.font(.headline)
|
||||
Text("Choose or create a service point (table) to provision a beacon for.")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Namespace Loading
|
||||
|
||||
@ViewBuilder
|
||||
private var namespaceLoadingView: some View {
|
||||
VStack(spacing: 16) {
|
||||
if isLoadingNamespace {
|
||||
ProgressView("Allocating beacon namespace…")
|
||||
} else {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView {
|
||||
Label("Namespace Error", systemImage: "exclamationmark.triangle")
|
||||
} description: {
|
||||
|
|
@ -151,6 +170,22 @@ struct ScanView: View {
|
|||
} actions: {
|
||||
Button("Retry") { Task { await loadNamespace() } }
|
||||
}
|
||||
} else {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.orange)
|
||||
Text("Namespace Error")
|
||||
.font(.headline)
|
||||
Text(errorMessage ?? "Failed to allocate beacon namespace")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
Button("Retry") { Task { await loadNamespace() } }
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
|
|
@ -245,11 +280,26 @@ struct ScanView: View {
|
|||
.padding()
|
||||
|
||||
if bleManager.discoveredBeacons.isEmpty && !bleManager.isScanning {
|
||||
if #available(iOS 17.0, *) {
|
||||
ContentUnavailableView(
|
||||
"No Beacons Found",
|
||||
systemImage: "antenna.radiowaves.left.and.right.slash",
|
||||
description: Text("Tap Scan to search for nearby beacons")
|
||||
)
|
||||
} else {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.font(.system(size: 48))
|
||||
.foregroundStyle(.secondary)
|
||||
Text("No Beacons Found")
|
||||
.font(.headline)
|
||||
Text("Tap Scan to search for nearby beacons")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.padding()
|
||||
}
|
||||
} else {
|
||||
List(bleManager.discoveredBeacons) { beacon in
|
||||
Button {
|
||||
|
|
@ -274,7 +324,7 @@ struct ScanView: View {
|
|||
Image(systemName: "light.beacon.max")
|
||||
.font(.system(size: 64))
|
||||
.foregroundStyle(.orange)
|
||||
.symbolEffect(.pulse)
|
||||
.modifier(PulseEffectModifier())
|
||||
|
||||
Text("Beacon is Flashing")
|
||||
.font(.title2.bold())
|
||||
|
|
@ -768,3 +818,16 @@ struct BeaconRow: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - iOS 16/17 Compatibility
|
||||
|
||||
/// Applies `.symbolEffect(.pulse)` on iOS 17+, no-op on iOS 16
|
||||
private struct PulseEffectModifier: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
if #available(iOS 17.0, *) {
|
||||
content.symbolEffect(.pulse)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue