Post

Replies

Boosts

Views

Activity

Reply to Testing In-App Purchases
With my implementation, I am just using SubscriptionStoreView() so I wasn't able to apply the link you shared. I tried something like this: .onChange(of: Product.PurchaseResult.success(<#VerificationResult<Transaction>#>)) { } However, I get the error: Instance method 'onChange(of:initial:_:)' requires that 'Product.PurchaseResult' conform to 'Equatable' Also, I'm not sure what the parameter VerificationResult is and how to access it.
Topic: Design SubTopic: General Tags:
Apr ’25
Reply to Testing In-App Purchases
Hi, I'm trying to implement the code provided in a function within an async function in ContentView. However, I get an error "Value of type 'ContentView' has no member 'checkVerified'"
Topic: Design SubTopic: General Tags:
May ’25
Reply to Background App Refresh
I have the following code in ContentView: .onAppear { BGTaskScheduler.shared.register(forTaskWithIdentifier: "appname.RoutineRefresh", using: nil) { task in print(task) handleAppRefresh(task: task as! BGAppRefreshTask) } } However, nothing prints. Additionally, I have print statements in scheduleAppRefresh and handleAppRefresh but nothing prints from those functions either. I am also calling scheduleTaskRefresh in a .task function.
Jun ’25
Reply to Background App Refresh
Could you elaborate on how to build a model layer and how that differentiates from calling a function from a .task or .onappear function within the view level? What do I need to pass to handleAppRefresh? import Foundation import SwiftData import SwiftUI import BackgroundTasks func scheduleAppRefresh() { let request = BGAppRefreshTaskRequest(identifier: "app.RoutineRefresh") // Fetch no earlier than 15 minutes from now. request.earliestBeginDate = Date(timeIntervalSinceNow: 60) do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule app refresh: \(error)") } } func handleAppRefresh(task: BGAppRefreshTask) async { // Schedule a new refresh task. scheduleAppRefresh() // Create an operation that performs the main part of the background task. let operation = RefreshAppContentsOperation() // Provide the background task with an expiration handler that cancels the operation. task.expirationHandler = { operation.cancel() } // Inform the system that the background task is complete // when the operation completes. operation.completionBlock = { task.setTaskCompleted(success: !operation.isCancelled) } // Start the operation. //operationQueue.addOperation(operation) } final class RefreshAppContentsOperation: Operation, @unchecked Sendable { @Environment(\.modelContext) private var modelContext override func main() { } }
Jun ’25
Reply to Testing In-App Purchases
When trying to use Product.PurchaseResult or Transaction.updates, or SubscriptionStatus.updates within an .onChange function I get the error: "Instance method 'onChange(of:initial:_:)' requires that 'Transaction.Transactions' conform to 'Equatable'" Also, despite clearing purchase history in settings it doesn't seem that the subscription is resetting.
Topic: Design SubTopic: General Tags:
Jun ’25
Reply to Background App Refresh
With the code below I get the error: class BackgroundTasksController: ObservableObject { func scheduleRoutineResetTask() { Task { BGTaskScheduler.shared.register(forTaskWithIdentifier: "app.BackgroundTask", using: nil) { task in Task { await self.handleAppRefresh(task: task as! BGAppRefreshTask) } } } } private func handleAppRefresh(task: BGAppRefreshTask) async { // Schedule a new refresh task. scheduleAppRefresh() // Create an operation that performs the main part of the background task. let operation = RefreshAppContentsOperation() // Provide the background task with an expiration handler that cancels the operation. task.expirationHandler = { operation.cancel() } // Inform the system that the background task is complete // when the operation completes. operation.completionBlock = { task.setTaskCompleted(success: !operation.isCancelled) } // Start the operation. //operationQueue.addOperation(operation) } private func scheduleAppRefresh() { let request = BGAppRefreshTaskRequest(identifier: "app.BackgroundTask") // Fetch no earlier than 15 minutes from now. request.earliestBeginDate = Date(timeIntervalSinceNow: 60) print(request) do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule app refresh: \(error)") } } } final class RefreshAppContentsOperation: Operation, @unchecked Sendable { } *** Assertion failure in -[BGTaskScheduler _unsafe_registerForTaskWithIdentifier:usingQueue:launchHandler:], BGTaskScheduler.m:225 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'All launch handlers must be registered before application finishes launching' *** First throw call stack: (0x18b5d321c 0x188a6dabc 0x18a8d1670 0x224da17f0 0x224da15f0 0x102fee124 0x102fee24d 0x102fefe1d 0x102feff79 0x19709d241) libc++abi: terminating due to uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'All launch handlers must be registered before application finishes launching' *** First throw call stack: (0x18b5d321c 0x188a6dabc 0x18a8d1670 0x224da17f0 0x224da15f0 0x102fee124 0x102fee24d 0x102fefe1d 0x102feff79 0x19709d241) terminating due to uncaught exception of type NSException Is this configuration proper, and with this configuration is.addOperation necessary?
Jun ’25
Reply to Testing In-App Purchases
With this code, I'm getting an error: import Foundation import Observation import StoreKit enum ProductID: String { case subscriptionMonthly = "app_monthly" case subscriptionYearly = "monthly_yearly" } @MainActor @Observable final class SubscriptionsHandler { public var activeSubscription: String? = nil init() { // Because the tasks below capture 'self' in their closures, this object must be fully initialized before this point. Task(priority: .background) { // Finish any unfinished transactions -- for example, if the app was terminated before finishing a transaction. for await verificationResult in Transaction.unfinished { await handle(updatedTransaction: verificationResult) } // Fetch current entitlements for all product types except consumables. for await verificationResult in Transaction.currentEntitlements { await handle(updatedTransaction: verificationResult) } } Task(priority: .background) { for await verificationResult in Transaction.updates { await handle(updatedTransaction: verificationResult) } } } private func handle(updatedTransaction verificationResult: VerificationResult<Transaction>) async { // The code below handles only verified transactions; handle unverified transactions based on your business model. guard case .verified(let transaction) = verificationResult else { return } if let _ = transaction.revocationDate { // Remove access to the product identified by `transaction.productID`. // `Transaction.revocationReason` provides details about the revoked transaction. guard let productID = ProductID(rawValue: transaction.productID) else { print("Unexpected product: \(transaction.productID).") return } activeSubscription = nil UserDefaults.standard.set(false, forKey: "isSubscribed") await transaction.finish() return } else if let expirationDate = transaction.expirationDate, expirationDate < Date() { // In an app that supports Family Sharing, there might be another entitlement that still provides access to the subscription. activeSubscription = nil UserDefaults.standard.set(false, forKey: "isSubscribed") return } else { // Provide access to the product identified by transaction.productID. guard let productID = ProductID(rawValue: transaction.productID) else { print("Unexpected product: \(transaction.productID).") return } activeSubscription = transaction.productID UserDefaults.standard.set(true, forKey: "isSubscribed") await transaction.finish() return } } } SwiftUICore/Environment+Objects.swift:34: Fatal error: No Observable object of type SubscriptionsHandler found. A View.environmentObject(_:) for SubscriptionsHandler may be missing as an ancestor of this view.
Topic: Design SubTopic: General Tags:
Jun ’25
Reply to Testing In-App Purchases
The SubscriptionsHandler class I have is observable and configured just like the documentation you provided so I'm not sure why I'm getting the error. Also, In the View, the SubscriptionsHandler class is called as an environment variable but is never used. Why is that?
Topic: Design SubTopic: General Tags:
Jun ’25
Reply to Background App Refresh
The app no longer crashes, but with my current configuration, the task does not run. I initialize the task scheduler in the App file's init() function, which calls handleAppRefresh(), which calls RefreshAppContentsOperation()At a given time I call scheduleAppRefresh(), which I thought would preform the task at the scheduled time. The function runs and prints this: <BGAppRefreshTaskRequest: app.Task, earliestBeginDate: 2025-07-01 21:38:43 +0000> All of these functions referenced are in my post above. I tried implementing a queue by doing this: let queue = OperationQueue() let operation = RefreshAppContentsOperation() queue.addOperation(operation) How can I resolve this by Queue or .task?
Jul ’25
Reply to Background App Refresh
I am getting the following error using the breakpoints below. I'm not sure if that indicates anything. It doesn't appear that RefreshAppContentsOperation runs. App.debug.dylib`partial apply for closure #1 in View.function(): -> 0x103aade10 <+0>: orr x29, x29, #0x1000000000000000 0x103aade14 <+4>: sub sp, sp, #0x20 0x103aade18 <+8>: stp x29, x30, [sp, #0x10] 0x103aade1c <+12>: str x22, [sp, #0x8] 0x103aade20 <+16>: add x29, sp, #0x10 0x103aade24 <+20>: ldr x9, [x22] 0x103aade28 <+24>: str x9, [sp] 0x103aade2c <+28>: mov x8, x29 0x103aade30 <+32>: sub x8, x8, #0x8 0x103aade34 <+36>: str x9, [x8] 0x103aade38 <+40>: ldr x0, [x9, #0x18] 0x103aade3c <+44>: ldr x8, [x22] 0x103aade40 <+48>: mov x10, x29 0x103aade44 <+52>: sub x10, x10, #0x8 0x103aade48 <+56>: str x8, [x10] 0x103aade4c <+60>: str x8, [x9, #0x10] 0x103aade50 <+64>: bl 0x103ad626c ; symbol stub for: swift_task_dealloc 0x103aade54 <+68>: ldr x8, [sp] 0x103aade58 <+72>: ldr x22, [x8, #0x10] 0x103aade5c <+76>: ldr x0, [x22, #0x8] 0x103aade60 <+80>: ldp x29, x30, [sp, #0x10] 0x103aade64 <+84>: and x29, x29, #0xefffffffffffffff 0x103aade68 <+88>: add sp, sp, #0x20 0x103aade6c <+92>: br x0 private func handleAppRefresh(task: BGAppRefreshTask) async { Task { let queue = OperationQueue() scheduleAppRefresh() // breakpoint print("test") // breakpoint // Create an operation that performs the main part of the background task. let operation = RefreshAppContentsOperation() // breakpoint // Provide the background task with an expiration handler that cancels the operation. task.expirationHandler = { operation.cancel() } // Inform the system that the background task is complete // when the operation completes. operation.completionBlock = { task.setTaskCompleted(success: !operation.isCancelled) } // Start the operation. queue.addOperation(operation) // breakpoint } } // Schedule a new refresh task. func scheduleAppRefresh() { let request = BGAppRefreshTaskRequest(identifier: "app.BackgroundTask") // Fetch no earlier than 15 minutes from now. request.earliestBeginDate = Date(timeIntervalSinceNow: 60) print(request) // breakpoint do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule app refresh: \(error)") } }
Jul ’25