Navigation angepasst, Absprung in angetippten song.

This commit is contained in:
2026-04-07 12:57:24 +02:00
parent 040917479e
commit fe3ed1e204
9 changed files with 361 additions and 4 deletions
@@ -0,0 +1,122 @@
//
// ServicesMAStoreManager.swift
// Mobile Music Assistant
//
// Created by Sven Hanold on 06.04.26.
//
import StoreKit
@Observable
@MainActor
final class MAStoreManager {
enum PurchaseResult: Equatable {
case success
case cancelled
case failed(String)
}
static let productIDs: Set<String> = [
"donate.song",
"donate.album",
"donate.anthology"
]
var products: [Product] = []
var isPurchasing = false
var purchaseResult: PurchaseResult?
private var transactionListener: Task<Void, Never>?
init() {
transactionListener = listenForTransactions()
}
// MARK: - Load Products
func loadProducts() async {
guard products.isEmpty else { return }
do {
let storeProducts = try await Product.products(for: Self.productIDs)
products = storeProducts.sorted { $0.price < $1.price }
} catch {
print("Failed to load products: \(error)")
}
}
// MARK: - Purchase
func purchase(_ product: Product) async {
isPurchasing = true
purchaseResult = nil
do {
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
await transaction.finish()
isPurchasing = false
purchaseResult = .success
case .userCancelled:
isPurchasing = false
purchaseResult = .cancelled
case .pending:
isPurchasing = false
@unknown default:
isPurchasing = false
}
} catch {
isPurchasing = false
purchaseResult = .failed(error.localizedDescription)
}
}
// MARK: - Transaction Listener
private func listenForTransactions() -> Task<Void, Never> {
Task.detached {
for await verificationResult in Transaction.updates {
if case .verified(let transaction) = verificationResult {
await transaction.finish()
}
}
}
}
// MARK: - Helpers
private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
switch result {
case .unverified(_, let error):
throw error
case .verified(let safe):
return safe
}
}
/// Returns the SF Symbol icon name for a given product ID
func iconName(for productID: String) -> String {
switch productID {
case "donate.song": return "music.note"
case "donate.album": return "opticaldisc"
case "donate.anthology": return "music.note.list"
default: return "gift"
}
}
/// Returns a friendly tier name for a given product ID
func tierName(for productID: String) -> String {
switch productID {
case "donate.song": return "Song"
case "donate.album": return "Album"
case "donate.anthology": return "Anthology"
default: return "Donation"
}
}
}