Navigation angepasst, Absprung in angetippten song.
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user