Hey y'all,
I'm reaching out because of an observed issue that I am experience both in sandbox and in production environments. This issue does not occur when using the Local StoreKit configurations.
For context, my app only implements auto-renewing subscriptions. I'm trying to track with my own analytics every time a successful purchase is made, whether in the app or externally through Subscription Settings. I'm seeming too many events for just one purchase.
My app is observing Transaction.updates. When I make a purchase with Product.purchase(_:), I successfully handle the purchase result. After about 10-20 seconds, I receive 2-3 new transactions in my Transaction.updates, even though I already handled and finished the Purchase result. This happens on production, where renewals are one week. This also happens in Sandbox, where at minimum renewals are every 3 minutes.
The transactions do not differ in transactionId, revocationDate, expirationDate, nor isUpgraded... so not sure why they're coming in through Transaction.updates if there are no "updates" to be processing.
For purchases made outside the app, I get the same issue. Transaction gets handled in updates several times.
Note that this is not an issue if a subscription renews. I use `Transaction.reason
I want to assume that StoreKit is a perfect API and can do no wrong (I know, a poor assumption but hear me out)... so where am I going wrong?
My current thought is a Swift concurrency issue. This is a contrived example:
// Assume Task is on MainActor
Task(priority: .background) { @MainActor in
for await result in Transaction.updates in {
// We suspend current process,
// so will we go to next item in the `for-await-in` loop?
// Because we didn't finish the first transaction, will we see it again in the updates queue?
await self.handle(result)
}
}
@MainActor
func handle(result) async {
...
await Analytics.sendEvent("purchase_success")
transaction.finish()
}
Delve into the world of built-in app and system services available to developers. Discuss leveraging these services to enhance your app's functionality and user experience.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
Hey y'all,
I'm reaching out because of an observed issue that I am experience both in sandbox and in production environments. This issue does not occur when using the Local StoreKit configurations.
For context, my app only implements auto-renewing subscriptions. I'm trying to track with my own analytics every time a successful purchase is made, whether in the app or externally through Subscription Settings. I'm seeming too many events for just one purchase.
My app is observing Transaction.updates. When I make a purchase with Product.purchase(_:), I successfully handle the purchase result. After a few seconds, I receive 2-3 new transactions in my Transaction.updates, even though I already handled and finished the Purchase result.
The transactions seem to have the same transactionId, and values such as revocationDate, expirationDate, and isUpgraded don't seem to change between any of them.
For purchases made outside the app, I get the same issue. Transaction gets handled in updates several times.
Note that this is not an issue if a subscription renews. I use `Transaction.reason
After upgrading to a new iPhone and restoring from an iCloud backup using the same Apple ID, I noticed an issue with Health app permissions.
■ What is happening
On my previous iPhone, an app had permission to read step count data.
After restoring to the new iPhone, the app still appears in the Health app under Sources.
However, when I tap the app, the usual data type permission toggles (such as Steps) are not displayed at all.
As a result, the app is unable to read step count data.
■ Additional details
The app itself seems to be recognized as a Health data source.
However, the data type permission screen is empty.
No ON/OFF switches are shown.
The backup was created on iOS 18, and the restore was performed on iOS 26.
I have not yet confirmed whether this also happens with other iOS version combinations.
■ Questions
Is it expected behavior that Health app permissions (per data type) are not restored via iCloud backup?
Has anyone experienced a similar situation where the app appears under Sources but the permission options are missing? If so, how did you resolve it?
Any information from users who have experienced the same issue would be greatly appreciated.
Starting in iOS 26.4, PushKit has introduced a new "didReceiveIncomingVoIPPushWithPayload" delegate, making it explicit whether or not an app is required to report a call for any given push. The new delegate passes in a PKVoIPPushMetadata object which includes a "mustReport" property.
We have not documented the exact criteria that will cause a mustReport to return false, but those criteria currently include:
The app being in the foreground at the point the push is received.
The app being on an active call at the point the push is received.
The system determines that delivery delays have made the call old enough that it may no longer be viable.
When mustReport is false, apps should call the PushKit completion handler (as they previously have) but are otherwise not required to take any other action.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware
Hi, we are setting up Apple Pay on our website which sells only digital goods. We don't collect a shipping address because we aren't shipping anything. We want to use the user's billing address in order to show them the total amount (including sales tax) before they authorize the purchase. However, it seems that the billing address isn't always provided by Apple Pay before the payment is authorized.
With Apple Pay, what is the recommended way of acquiring the user's billing address before they authorize the purchase?
--
More details about our setup:
We are using the Apple Pay JS API.
In createPaymentRequest, we specify requiredBillingContactFields: ['postalAddress'], but per the docs, the address is provided after the user authorizes the transaction. That is too late for us because we want to show the sales tax before the user authorizes the purchase.
We have attempted to work around this by getting the billing contact details in session.onpaymentmethodselected. For example:
session.onpaymentmethodselected = function (event) {
const billingContact = event.paymentMethod.billingContact;
// Sometimes `billingContact` exists, but other times it does not
}
This doc states:
Before the user authorizes the transaction, you receive redacted billing contact information in a callback event. The redacted information includes only the necessary data for completing transaction tasks, such as calculating taxes or shipping costs.
But in practice, we've observed that sometimes no billing contact information is provided. When a user switches from one card to another, we seem to never get the billing contact associated with the newly selected card.
Is there something we're missing?
The scenario is, in a macOS app (primarly), main thread needs to wait for some time for a certain 'event'. When that event occurs, the main thread is signaled, it gets unblocked and moves on.
An example is, during shutdown, a special thread known as shutdown thread waits for all other worker threads to return (thread join operation). When all threads have returned, the shutdown thread signals the main thread, which was waiting on a timer, to continue with the shutdown flow. If shutdown thread signals the main thread before the later's timer expires, it means all threads have returned. If main thread's timer expires first, it means some threads have failed to join (probably stuck in infinite loop due to bug, disk I/O etc.).
This post is to understand how main thread can wait for some time for the shutdown thread. There are two ways: a) dispatch_semaphore_t b) pthread conditional variable (pthread_cond_t) and mutex (pthread_mutex_t).
Expanding a bit on option (b) using conditional variable and mutex:
// This method is invoked on the main thread
bool ConditionSignal::TimedWait() noexcept
{
struct timespec ts;
pthread_mutex_t * mutex = (pthread_mutex_t *) (&vPosix.vMutexStorage[0]);
pthread_cond_t * cond = (pthread_cond_t *) (&vPosix.vCondVarStorage[0]);
// Set the timer to 3 sec.
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 3;
pthread_mutex_lock(mutex);
LOG("Main thread has acquired the mutex and is waiting!");
int wait_result = pthread_cond_timedwait(cond, mutex, &ts);
switch (wait_result) {
case 0:
LOG("Main thread signaled!");
return true;
case ETIMEDOUT:
LOG("Main thread's timer expired!");
return false;
default:
LOG("Error: Unexpected return value from pthread_cond_timedwait: " + std::to_string(wait_result));
break;
}
return false;
}
// This method is invoked on shutdown thread after all worker threads have returned.
void ConditionSignal::Raise() noexcept
{
pthread_mutex_t * mutex = (pthread_mutex_t *) (&vPosix.vMutexStorage);
pthread_cond_t * cond = (pthread_cond_t *) (&vPosix.vCondVarStorage);
pthread_mutex_lock(mutex);
LOG("[Shutdown thread]: Signalling main thread...");
pthread_cond_signal(cond);
pthread_mutex_unlock(mutex);
}
Both options allow the main thread to wait for some time (for shutdown thread) and continue execution. However, when using dispatch_semaphore_t, I get the following warning:
Thread Performance Checker: Thread running at User-interactive quality-of-service class waiting on a lower QoS thread running at Default quality-of-service class. Investigate ways to avoid priority inversions
Whereas, with conditional variables, there are no warnings. I understand the warning - holding the main thread can prevent it from responding to user events, thus causing the app to freeze. And in iOS, the process is killed if main thread takes more than 5 secs to return from applicationWillTerminate(_:) delegate method. But in this scenario, the main thread is on a timed-wait, for some milliseconds i.e., it is guaranteed to not get blocked indefinitely.
While this is described for macOS, this functionality is required for all Apple OSes. What is the recommend way?
Hi everyone,
On macOS 26.4 beta (with Xcode 26.4 beta), I’m seeing the following console messages in a brand new SwiftData + CloudKit template project (no custom logic added, fresh CloudKit container):
updateTaskRequest called for a pre-running task com.apple.coredata.cloudkit.activity.export.F9EE783D-7521-4EC2-B42C-9FD1F29BA5C4
updateTaskRequest called for an already running/updated task com.apple.coredata.cloudkit.activity.export.F9EE783D-7521-4EC2-B42C-9FD1F29BA5C4
Error updating background task request: Error Domain=BGSystemTaskSchedulerErrorDomain Code=8 "(null)"
These messages appear:
When CloudKit is enabled
Occasionally on app launch
Often when bringing the app back to the foreground (Cmd-Tab away and back)
Even with zero additional SwiftData logic
They do not appear when CloudKit is disabled.
This behavior is reproducible on a completely new project with a fresh CloudKit container.
Questions:
What exactly do these messages indicate?
Is BGSystemTaskScheduler Code=8 expected in this context?
Are these safe to ignore?
Is this a known change in logging behavior in macOS 26.4 beta?
Additionally, in a larger project I’ve observed SwiftData crashes and initially suspected these logs might be related. However, since the issue reproduces in a fresh template project, I’m unsure whether this is simply verbose beta logging or something more serious.
Any clarification would be appreciated.
Filed as FB21993521.
Environment
OS: iOS 26.2 ~ 26.3
SDK: Xcode 16.4 (Target: iOS 17.6)
Framework: StoreKit 2
Environment: Production (Cannot reproduce in Sandbox or Xcode Configuration)
Issue Description
We are encountering a critical purchase failure that occurs exclusively in the Production environment.
When a user who has a "Pending Downgrade" (scheduled for the next renewal date) attempts to re-purchase their current higher-tier product to cancel the downgrade, StoreKit 2 returns an error.
Steps to Reproduce
User is currently on "Product A" (Higher Tier).
User schedules a downgrade to "Product B" (Lower Tier). The status changes to "Pending Downgrade".
User attempts to purchase "Product A" again via Product.purchase().
The system purchase sheet appears, and the user confirms the purchase.
Immediately after authentication, a system alert from StoreKit appears saying: "Cannot process request at this time. Please try again later." (現在リクエストを一時的に処理できません。しばらくしてからもう一度お試しください。)
After dismissing the alert, the app receives StoreKitError code 2 (unknown) with the localized message: "Request could not be completed" (リクエストを完了できません).
Technical Observations
Transaction.currentEntitlements: Does not change.
App Store Server Notifications (V2): No notifications are sent to our server.
Sandbox Behavior: Works perfectly. Re-purchasing Product A successfully cancels the downgrade and the subscription remains at the Higher Tier.
AppStore.sync(): Running a manual sync does not resolve the pending state after the error.
Question
Since we cannot debug production-level logs, we are stuck. Is this a known regression in the StoreKit 2 commerce engine regarding state synchronization for downgrades? Has anyone found a workaround for this specific scenario?
Any insights would be greatly appreciated.
I've been able to run this sample project with the PIRServer. But the urls are still not blocked.
https://developer.apple.com/documentation/networkextension/filtering-traffic-by-url
https://github.com/apple/pir-service-example
I got this on the log
Received filter status change: <FilterStatus: 'running'>
I'm debugging an app I'm building and everything I'm seeing suggests I need to put in a request to Apple to turn on NetworkExtension → Packet Tunnel Provider entitlement for our Team ID and bundle IDs.
1: Is this true?
2: Doesn't the option in xcode handle this?
Hello,
Recently we got a question about the event_timestamps for the phone usage report in our research project:
Does the event_timestamp correspond to the start point or end point of a period of interest?
For example, say the event_timestamp is 2024-07-01 23:59:59 with a duration of 86400. The user is noted to have made 2 phone calls during that event_timestamp. Did those phone calls occur in the 86400 seconds prior to that timestamp (i.e., on 2024-07-01) or in the 86400 seconds after that timestamp (i.e., on 2024-07-02)?
Thanks!
Hi, I’m working on a proof of concept for a CarPlay application where I need to display multiple Points of Interest, such as EV charging stations and fuel stations. The app will also allow users to book a selected Point of Interest (for example, an EV charging slot). I understand that the app can be registered under the Navigation (turn‑by‑turn) category, but I wanted to check whether it is also possible to register it under the Driving Task category. I also want to know if i can Register for multiple Categories for my car play application.
I am developing a mobile application using Flutter and plan to implement in-app subscriptions for both iOS platforms. I would like to request guidance on the following:
Technical Implementation:
Recommended best practices for implementing auto-renewable subscriptions in Flutter apps
StoreKit 2 integration requirements and compatibility considerations
Server-to-server notification setup and endpoint requirements
Testing Procedures:
Sandbox environment configuration and testing workflow
TestFlight testing requirements for subscription features
Recommended testing scenarios before production release
Required Documentation and Accounts:
Complete list of required agreements (Paid Applications Agreement, etc.)
Banking and tax information requirements
Privacy policy and terms of service specifications for subscription apps
App Review guidelines specific to subscription-based apps
Subscription Management:
Grace period implementation requirements
Handling subscription cancellations and refunds
Promotional offers and introductory pricing setup
Could you please provide documentation or direct me to the appropriate resources? Additionally, if there are any specific requirements for Flutter-based applications, I would appreciate that information.
Application Details:
Platform: iOS (Flutter framework)
Subscription Type: Auto-renewable subscriptions
I have noticed that there are no new kdk builds since mid December
Is everything ok at apple
I am aware of NXUs but the kernel version has changed much since then
I'm building a HomeKit app that discovers Thread devices and visualizes the mesh topology. I can detect device roles (Router vs End Device via characteristic 0x0703) and identify Border Routers (via _meshcop._udp), but I cannot determine which Router is the parent of a given End Device. Any Thread device can act as a Router (a Nanoleaf bulb, an Eve plug, not just HomePods), and End Devices attach to these Routers as children. That parent-child relationship is what I'm trying to map, but there's no RLOC16, neighbor table, or parent identifier exposed through any available API.
I've tested every path I can find. Here's what I've tried on a network with 44 Thread devices and 6 Border Routers:
What works (partially)
HAP Thread Management Service (0x0701) gives me the device role from characteristic 0x0703, the OpenThread version from 0x0706, and node capabilities from 0x0702. That's the complete set of characteristics on that service. None of them contain RLOC16, parent Router, or neighbor data. This service also only exists on HAP-native Thread devices. My 20 Matter-over-Thread devices (Aqara, Eve Door, SmartWings, Onvis S4) don't have it at all.
MeshCoP Bonjour (_meshcop._udp) identifies Border Routers and the network name/Extended PAN ID. No topology data about other mesh nodes.
What doesn't work
ThreadNetwork framework (THClient) - retrieveAllCredentials() returns error Code 3 because the app can't access credentials stored by Apple Home. Even if it worked, THCredentials only contains network config (name, PAN ID, channel), not topology.
Direct CoAP queries - Border Routers don't route traffic from WiFi to Thread management ports. Mesh-local addresses aren't reachable. No Thread NWInterface in Network.framework.
Network.framework - No visibility into the Thread mesh from the WiFi side.
The only remaining path I can see (but it's not practical)
Matter cluster 0x0035 (Thread Network Diagnostics) appears to have exactly what I need: RLOC16, NeighborTable with isChild boolean, RouteTable. I haven't implemented this because it requires commissioning each device individually onto my app's own Matter fabric via Multi-Admin. That's 21 separate user-initiated pairing actions on my network. I can't ask end users to do that.
The core issue
Every Thread Router (whether it's a HomePod acting as a Border Router or a Nanoleaf bulb acting as a mesh Router) knows its own children and neighbors. The Border Routers also maintain route tables covering the mesh backbone. This data exists on the user's own devices but none of it is exposed to third-party apps.
Even something minimal would help. HMAccessory already exposes matterNodeID as a cross-protocol identifier. Exposing RLOC16 the same way would be enough, since parent-child relationships are encoded in the address itself (ParentRLOC = ChildRLOC & 0xFC00).
Has anyone found another approach I'm missing?
Thanks in advance for any pointers.
I have a Live Activity on iOS. Love it.
However, that Live Activity via remote hosting (I'm not building for other targets) shows the Live Activity on watchOS, macOS and CarPlay.
disfavoredLocations are for widgets, but I don't appear to have a method to limit or provide exclusive locations the live activity should appear.
https://developer.apple.com/documentation/DeclaredAgeRange/AgeRangeService/isEligibleForAgeFeatures returns a bool. I assume that means that it will return True for the states where their laws are in effect.
The TX law and the UT/LA/AZ laws have different requirements though:
TX requires the app verify the user's age on every app launch.
These other states require the app verify the user's age "no more than once during each 12-month period"
A future law (Brazil maybe?) might do something else.
How can we determine if the user is eligible for the TX versus other state requirements?
Environment:
iPhone 17 / iPhone 17 Pro (Apple N1 chip)
iOS 26.x
Xcode 26
Framework: Flutter app with native iOS BLE library (CoreBluetooth)
We have a production IoT app that communicates with BLE nodes (Nordic, PIC, EnOcean peripherals) using an advertising/scanning-based protocol — not GATT connections. The app broadcasts commands via CBPeripheralManager (advertising service UUIDs) and receives responses by scanning with CBCentralManager (reading manufacturer data and service UUIDs from advertisement packets). This workflow has been reliable across all iPhone models from iPhone 8 through iPhone 16 Pro Max.
On iPhone 17 devices, we are experiencing multiple failures in this workflow.
Architecture:
Sending commands: We use CBPeripheralManager.startAdvertising() with CBAdvertisementDataServiceUUIDsKey to broadcast a UUID-encoded command to nearby nodes.
Receiving responses: We use CBCentralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true]) and filter responses in centralManager(_:didDiscover:advertisementData:rssi:) by matching CBAdvertisementDataServiceUUIDsKey or CBAdvertisementDataManufacturerDataKey against expected UUID masks.
Communication pattern: Advertise a command → stop advertiser → start scanner → wait for matching response → process result. Typical timeout is 1.5 seconds per exchange.
Issues observed on iPhone 17:
peripheralManagerDidStartAdvertising behaviour change
After calling CBPeripheralManager.startAdvertising(:), the delegate callback peripheralManagerDidStartAdvertising(:error:) either fires with errors that did not occur on previous hardware, or advertising does not appear to reach the peripheral nodes at all. The same advertising payload works immediately when tested on iPhone 15/16.
Is the N1 chip's Bluetooth 6 stack handling CBAdvertisementDataServiceUUIDsKey advertising differently? Are there new constraints on advertising payload size or format?
Scanner returning fewer/no results with withServices: nil
Our scanner uses scanForPeripherals(withServices: nil) because we need to read manufacturer data from advertisement packets and filter using a custom UUID mask. On iPhone 17, we observe significantly fewer didDiscover callbacks compared to iPhone 15/16 in the same physical environment, with the same nodes advertising.
We understand that passing service UUIDs in withServices: is recommended, but our protocol requires reading raw manufacturer data bytes that aren't associated with a single service UUID — we use mask-based matching (e.g., filter mask 11110000-0000-0000-0000-000000000000 against scan results).
Has the N1 chip changed the rate or filtering behaviour of unfiltered BLE scans? Is there a new throttling mechanism?
Background scanning stops immediately
When the app moves to background, scanning appears to stop entirely on iPhone 17 — even with bluetooth-central in UIBackgroundModes. On iPhone 16, background scanning continued (at reduced intervals) and delivered results for peripherals advertising filtered service UUIDs.
Aggressive session termination on app backgrounding
Our advertise-then-scan sequences (typically 1.5s round-trip) are being interrupted when the user briefly switches apps. The CBPeripheralManager stops advertising and the CBCentralManager stops scanning, causing timeout errors. This was not observed on previous iPhone models with the same iOS background mode configuration.
Questions for Apple:
Are there documented changes to CoreBluetooth behaviour on the N1 Bluetooth 6 chip that affect advertising-based (non-GATT) communication patterns?
Has the scan response rate for scanForPeripherals(withServices: nil) been intentionally reduced on iPhone 17?
Is CBCentralManagerOptionRestoreIdentifierKey now required for reliable background scanning on iPhone 17, or is this a known regression?
Are there new advertising payload constraints (size, format, interval) that we should be aware of for the N1 chip?
What we've tried:
Added NSBluetoothAlwaysUsageDescription and NSBluetoothWhileInUseUsageDescription to Info.plist
Confirmed Bluetooth permissions are granted
Tested with identical BLE nodes that work on iPhone 15/16
Verified CBManagerState.poweredOn before all operations
Any guidance or known workarounds would be greatly appreciated. Happy to provide sysdiagnose logs or a minimal reproducible sample project.
We are experiencing an issue where Apple’s CDN is not fetching the updated apple-app-site-association (AASA) file correctly for our domain.
Domain - app.myloft-stage.com
AASA File Locations (Both Return Correct JSON):
https://app.myloft-stage.com/.well-known/apple-app-site-association
https://app.myloft-stage.com/apple-app-site-association
Both endpoints:
Return HTTP 200
Return valid JSON
Content-Type: application/json
No redirects
Valid SSL certificate
JSON validated and correctly formatted
Apple CDN URL -
https://app-site-association.cdn-apple.com/a/v1/app.myloft-stage.com
Error Returned by Apple CDN - {"cause":"invalid character '\u003c' looking for beginning of value"}
This error indicates that Apple CDN is receiving HTML content (starting with <) instead of JSON, even though the origin server returns proper JSON.
Observations :
Direct access to AASA file returns correct JSON.
Apple CDN appears to be caching an older or incorrect response.
The CDN response does not match the current server response.
Universal Links fail due to this incorrect AASA retrieval.
Hi,
I've now identified a few areas when BGContinuedProcessingTask gets expired by the system
no progress for ~30 seconds
high CPU usage
high temperature
Some of these I can preempt and expire preemptively and handle the notification, others I cannot and just need to let the failure bubble up.
When the failure does bubble up, I'd like to update the title and subtitle. I'm able to update the title, but the subtitle is fixed at "Task Failed"
Is there any workaround? Or shall I file a bug here?