Team-scoped keys introduce the ability to restrict your token authentication keys to either development or production environments. Topic-specific keys in addition to environment isolation allow you to associate each key with a specific Bundle ID streamlining key management.
For detailed instructions on accessing these features, read our updated documentation on establishing a token-based connection to APNs.
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
Activity
Hello,
We are having an issue with the RequestReview API and were hoping to get some help. We know that there is no guarantee that the in-app review modal will show and we know that there are 3 circumstances in which it will definitely not appear:
if the user has turned off in-app review/ratings in their settings
if the user has submitted a review for that app on that device within the last 365 days
if the user has been asked for a review >3 times in the last 365 days
When testing our implementation, every single one of our testers did not receive the rating modal despite the fact that we had all our testers turn on the app rating setting and that we have never asked for reviews from our app before. So that seems suspicious. While it is possible that something is up with our code (and I have provided some snippets below) we are also concerned that apple maybe is suppressing it for another reason. We really want to go live with our app review code but unfortunately we are not able to get confidence that it will ever appear for the user. Can you please help us understand why this isn't working.
The code: We are using the SwiftUI approach to requesting review. Here are some relevant code snippets
Important to note, we have a modal that appears when the user is in our list of active, targeted users. If they tap yes on this modal, it should show the in app rate the app system modal. If they tap no, we present them with an airship survey so that they can give feedback. Here is the code for the Yes button action:
@Environment(\.requestReview) private var requestReview
private var yesButton: some View {
Button(
action: {
dismiss()
requestReview()
},
label: {
Text(Lingua.General.appRateFirstButton)
.regularParagraph()
.frame(width: 180, height: 35)
}
)
.customButtonStyle(
foregroundColor: .black,
backgroundColor: Color(.powderBlue),
radius: 36
)
}
and this is the logic we use to determine whether we want to show them the modal in the first place. Obviously, a lot of this code leads to deeper areas in our logic and code but to give an idea...
private func showAppRateModalIfNeeded() {
if preferencesManager.appRateReviewShown == nil,
accountManager.userAccount?.permissions.rateTheApp == true {
let appReviewModalVC = UIHostingController(rootView: AppReviewModal())
appReviewModalVC.view.backgroundColor = .init(white: 0, alpha: 0.6)
appReviewModalVC.modalPresentationStyle = .overFullScreen
appReviewModalVC.modalTransitionStyle = .crossDissolve
parentVC?.navigationController?.present(appReviewModalVC, animated: true)
preferencesManager.appRateReviewShown = true
}
}
When testing in debug, we do find that the modal appears and works as expected. However, on release builds nobody is able to trigger it. Why? Are we doing something wrong here or is Apple just suppressing it. We are thinking about implementing the button taking the user directly into the app store review but we'd prefer to do the lower-friction dialog in-app if we can get it work so the user doesn't get sent out of the app.
Topic:
App & System Services
SubTopic:
Core OS
Hi everyone, could you help us?
We implemented a Flutter library that basically makes a call every x minutes if the app is in the background, but when I generate the version via TestFlight for testing, it doesn't work.
Can you help us understand why?
Below is a more detailed technical description.
Apple Developer Technical Support Request
Subject: BGTaskScheduler / Background Tasks Not Executing in TestFlight - Flutter App with workmanager Plugin
Issue Summary
Background tasks scheduled using BGTaskScheduler are not executing when the app is distributed via TestFlight. The same implementation works correctly when running the app locally via USB/Xcode debugging.
We are developing a Flutter application that needs to perform periodic API calls when the app is in the background. We have followed all documentation and implemented the required configurations, but background tasks are not being executed in the TestFlight build.
App Information
Field
Value
App Version
3.1.15 (Build 311)
iOS Minimum Deployment Target
iOS 15.0
Framework
Flutter
Flutter SDK Version
^3.7.2
Technical Environment
Flutter Dependencies (Background Task Related)
Package
Version
Purpose
workmanager
^0.9.0+3
Main background task scheduler (uses BGTaskScheduler on iOS 13+)
flutter_background_service
^5.0.5
Background service management
flutter_background_service_android
^6.2.4
Android-specific background service
flutter_local_notifications
^19.4.2
Local notifications for background alerts
timezone
^0.10.0
Timezone support for scheduling
Other Relevant Flutter Dependencies
Package
Version
firebase_core
4.0.0
firebase_messaging
(via native Podfile)
sfmc (Salesforce Marketing Cloud)
^9.0.0
geolocator
^14.0.0
permission_handler
^12.0.0+1
Info.plist Configuration
We have added the following configurations to Info.plist:
UIBackgroundModes
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>remote-notification</string>
<string>processing</string>
</array>
### BGTaskSchedulerPermittedIdentifiers
```xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>br.com.unidas.apprac.ios.workmanager.carrinho_api_task</string>
<string>br.com.unidas.apprac.ios.workmanager</string>
<string>be.tramckrijter.workmanager.BackgroundTask</string>
</array>
**Note:** We included multiple identifier formats as recommended by the `workmanager` Flutter plugin documentation:
1. `{bundleId}.ios.workmanager.{taskName}` - Custom task identifier
2. `{bundleId}.ios.workmanager` - Default workmanager identifier
3. `be.tramckrijter.workmanager.BackgroundTask` - Plugin's default identifier (as per plugin documentation)
## AppDelegate.swift Configuration
We have configured the `AppDelegate.swift` with the following background processing setup:
```swift
// In application(_:didFinishLaunchingWithOptions:)
// Configuration to enable background processing via WorkManager
// The "processing" mode in UIBackgroundModes allows WorkManager to use BGTaskScheduler (iOS 13+)
// This is required to execute scheduled tasks in background (e.g., API calls)
// Note: User still needs to have Background App Refresh enabled in iOS settings
if UIApplication.shared.backgroundRefreshStatus == .available {
// Allows iOS system to schedule background tasks with minimum interval
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
}
## WorkManager Implementation (Dart/Flutter)
### Initialization
```dart
/// Initializes WorkManager
static Future<void> initialize() async {
await Workmanager().initialize(callbackDispatcher, isInDebugMode: false);
print('WorkManagerService: WorkManager initialized');
}
### Task Registration
/// Schedules API execution after a specific delay
## Observed Behavior
### Works (Debug/USB Connection)
- When running the app via Xcode/USB debugging
- Background tasks are scheduled and executed as expected
- API calls are made successfully when the app is backgrounded
### Does NOT Work (TestFlight)
- When the app is distributed via TestFlight
- Background tasks appear to be scheduled (no errors in code)
- Tasks are **never executed** when the app is in background
- We have tested with:
- Background App Refresh enabled in iOS Settings
- App used frequently
- Device connected to WiFi and charging
- Waited for extended periods (hours)
## Possible heart points
1. **Are there any additional configurations required for `BGTaskScheduler` to work in TestFlight/Production builds that are not required for debug builds?**
2. **Is the identifier format correct?** We are using:
`br.com.unidas.apprac.ios.workmanager.carrinho_api_task`
- Should it match exactly with the task name registered in code?
3. **Are there any known issues with Flutter's `workmanager` plugin and iOS BGTaskScheduler in production environments?**
4. **Is there any way to verify through logs or system diagnostics if the background tasks are being rejected by the system?**
5. **Could there be any conflict between our other background modes (`location`, `remote-notification`) and `processing`?**
6. **Does the Salesforce Marketing Cloud SDK (SFMC) interfere with BGTaskScheduler operations?**
## Additional Context
- We have verified that `Background App Refresh` is enabled for our app in iOS Settings
- The app has proper entitlements for push notifications and location services
- Firebase, SFMC (Salesforce Marketing Cloud), and other SDKs are properly configured
- The issue is **only** present in TestFlight builds, not in debug/USB-connected builds
## References
- [Apple Documentation - BGTaskScheduler](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler)
- [Apple Documentation - Choosing Background Strategies](https://developer.apple.com/documentation/backgroundtasks/choosing_background_strategies_for_your_app)
Thank you
Topic:
App & System Services
SubTopic:
Processes & Concurrency
I have an iOS app that allows user to select a folder (from Files). I want to bookmark that folder and later on (perhaps on a different launch of the app) access the contents of it. Is that scenario supported or not? Can't make it work for some reason (e.g. I'm getting no error from the call to create a bookmark, from a call to resolve the bookmark, the folder URL is not stale, but... startAccessingSecurityScopedResource() is returning false.
Hello to all
I have coded in swift a headless app, that launches 3 go microservices and itself. The app listens via unix domain sockets for commands from the microservices and executes different VPN related operations, using the NEVPNManager extension. Because there are certificates and VPN operations, the headless app and two Go microservices must run as root.
The app and microservices run perfectly when I run in Xcode launching the swift app as root. However, I have been trying for some weeks already to modify the application so at startup it requests the password and runs as root or something similar, so all forked apps also run as root. I have not succeeded. I have tried many things, the last one was using SMApp but as the swift app is a headless app and not a CLI command app it can not be embedded. And CLI apps can not get the VPN entitlements. Can anybody please give me some pointers how can I launch the app so it requests the password and runs as root in background or what is the ideal framework here? thank you again.
When trying to check if a certificate has been revoked with SecPolicyCreateRevocation (Flags: kSecRevocationUseAnyAvailableMethod | kSecRevocationRequirePositiveResponse) and SecTrustEvaluateWithError I always get the result error code errSecIncompleteCertRevocationCheck, regardless if the certificate was revoked or not.
Reproduction: Execute the program from the attached Xcode project (See Feedback FB21224106).
Error output:
Error: Error Domain=NSOSStatusErrorDomain Code=-67635 ""revoked.badssl.com","E8","ISRG Root X1" certificates do not meet pinning requirements" UserInfo={NSLocalizedDescription="revoked.badssl.com","E8","ISRG Root X1" certificates do not meet pinning requirements, NSUnderlyingError=0x6000018d48a0 {Error Domain=NSOSStatusErrorDomain Code=-67635 "Certificate 0 “revoked.badssl.com” has errors: Failed to check revocation;" UserInfo={NSLocalizedDescription=Certificate 0 “revoked.badssl.com” has errors: Failed to check revocation;}}}
To me it looks like that the revocation check just fails („Failed to check revocation;“), no further information is provided by the returned error.
In the example the certificate chain of https://revoked.badssl.com (default code) and https://badssl.com is verified (to switch see comments in the code).
I have a proxy configured in the system, I assume that the revocation check will use it.
On the same machine, the browsers (Safari and Google Chrome) can successfully detect if the certificate was revoked (revoked.badssl.com) or not (badssl.com) without further changes in the system/proxy settings.
Note: The example leaks some memory, it’s just a test program.
Am I missing something?
Feedback: FB21224106
Hi Team, I want to perform bluetooth advertising (no need to pair) from a macOS machine even before the user login to the macOS(i.e before user provide password and submit). Is there a way to achieve this?
I’m looking for an authoritative answer on how BGAppRefreshTask behaves after a user force-quits an app (swipes it away in the App Switcher).
My app relies on early-morning background refresh to prepare and schedule notifications based on user-defined thresholds and weather forecasts.
Behavior across devices seems inconsistent, however: sometimes a scheduled background refresh still runs, and other times it appears completely blocked.
Apple’s documentation doesn’t clearly state what should happen, and developer discussions conflict.
Could someone from Apple please clarify:
Will a previously scheduled BGAppRefreshTask run after the user force-quits the app?
If not, is there a recommended alternative for time-sensitive updates that must schedule user alerts?
What is the expected system behavior regarding the predictability of background refresh after a force-quit?
A definitive answer would help ensure the app aligns with intended system behavior.
Thanks!
When an iOS device is connected to a Bluetooth accessory that utilizes the Hands-Free Profile (HFP), we are encountering an incorrect audio routing behavior specifically for system notification tones.
Accessory Connected: The iOS device is successfully connected to a Bluetooth accessory (specifically, a WM500 device) using the HFP profile for voice communication.
Voice Audio: Audio streams related to phone calls or voice communication (using the HFP/SCO link) are correctly routed to the WM500.
Notification Tones Issue: System notification tones, which are played using the tonetype.systemsounds API, are not being routed to the connected HFP accessory (WM500). Instead, they are incorrectly played through the iOS device's built-in speaker.
Accessory team has suggested to establish SCO connection to route the tones through WM500.
But iOS does not provide an external API (like Android's startBluetoothSco) to explicitly force the establishment of an SCO connection for notification tones.
Is there any other approach to establish SCO connection in iOS to route notification tones through WM500
The same code built in a regular Mac app (with UI) does get paired.
The characteristic properties are [.read, .write, .notify, .notifyEncryptionRequired]
The characteristic permissions are [.readEncryptionRequired, .writeEncryptionRequired]
My service is primary.
In the iOS app (central) I try to read the characteristic, but an error is reported: Error code: 5, Description: Authentication is insufficient.
Topic:
App & System Services
SubTopic:
Processes & Concurrency
Tags:
Service Management
Core Bluetooth
Hi,
We’re seeing our build system (Gradle) get stuck in sendto system calls while trying to communicate with other processes via the local interface over UDP. To the end user it appears that the build is stuck or they will receive an error “Timeout waiting to lock XXX. It is currently in use by another Gradle instance”. But when the process is sampled/profiled, we can see one of the threads is stuck in a sendto system call. The only way to resolve the issue is to kill -s KILL <pid> the stuck Gradle process.
A part of the JVM level stack trace:
"jar transforms Thread 12" #90 prio=5 os_prio=31 cpu=0.85ms elapsed=1257.67s tid=0x000000012e6cd400 nid=0x10f03 runnable [0x0000000332f0d000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.DatagramChannelImpl.send0(java.base@17.0.10/Native Method)
at sun.nio.ch.DatagramChannelImpl.sendFromNativeBuffer(java.base@17.0.10/DatagramChannelImpl.java:901)
at sun.nio.ch.DatagramChannelImpl.send(java.base@17.0.10/DatagramChannelImpl.java:863)
at sun.nio.ch.DatagramChannelImpl.send(java.base@17.0.10/DatagramChannelImpl.java:821)
at sun.nio.ch.DatagramChannelImpl.blockingSend(java.base@17.0.10/DatagramChannelImpl.java:853)
at sun.nio.ch.DatagramSocketAdaptor.send(java.base@17.0.10/DatagramSocketAdaptor.java:218)
at java.net.DatagramSocket.send(java.base@17.0.10/DatagramSocket.java:664)
at org.gradle.cache.internal.locklistener.FileLockCommunicator.pingOwner(FileLockCommunicator.java:61)
at org.gradle.cache.internal.locklistener.DefaultFileLockContentionHandler.maybePingOwner(DefaultFileLockContentionHandler.java:203)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock$1.run(DefaultFileLockManager.java:380)
at org.gradle.internal.io.ExponentialBackoff.retryUntil(ExponentialBackoff.java:72)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.lockStateRegion(DefaultFileLockManager.java:362)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.lock(DefaultFileLockManager.java:293)
at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.<init>(DefaultFileLockManager.java:164)
at org.gradle.cache.internal.DefaultFileLockManager.lock(DefaultFileLockManager.java:110)
at org.gradle.cache.internal.LockOnDemandCrossProcessCacheAccess.incrementLockCount(LockOnDemandCrossProcessCacheAccess.java:106)
at org.gradle.cache.internal.LockOnDemandCrossProcessCacheAccess.acquireFileLock(LockOnDemandCrossProcessCacheAccess.java:168)
at org.gradle.cache.internal.CrossProcessSynchronizingCache.put(CrossProcessSynchronizingCache.java:57)
at org.gradle.api.internal.changedetection.state.DefaultFileAccessTimeJournal.setLastAccessTime(DefaultFileAccessTimeJournal.java:85)
at org.gradle.internal.file.impl.SingleDepthFileAccessTracker.markAccessed(SingleDepthFileAccessTracker.java:51)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.markAccessed(DefaultCachedClasspathTransformer.java:209)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.transformFile(DefaultCachedClasspathTransformer.java:194)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.lambda$cachedFile$6(DefaultCachedClasspathTransformer.java:186)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer$$Lambda$368/0x0000007001393a78.call(Unknown Source)
at org.gradle.internal.UncheckedException.unchecked(UncheckedException.java:74)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer.lambda$transformAll$8(DefaultCachedClasspathTransformer.java:233)
at org.gradle.internal.classpath.DefaultCachedClasspathTransformer$$Lambda$372/0x0000007001398470.call(Unknown Source)
at java.util.concurrent.FutureTask.run(java.base@17.0.10/FutureTask.java:264)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@17.0.10/ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@17.0.10/ThreadPoolExecutor.java:635)
at java.lang.Thread.run(java.base@17.0.10/Thread.java:840)
A part of the process sample:
2097 Thread_3879661: Java: jar transforms Thread 12
+ 2097 thread_start (in libsystem_pthread.dylib) + 8 [0x18c42eb80]
...removed for brevity...
+ 2097 Java_sun_nio_ch_DatagramChannelImpl_send0 (in libnio.dylib) + 84 [0x102ef371c]
+ 2097 __sendto (in libsystem_kernel.dylib) + 8 [0x18c3f612c]
We have observed the following system logs around the time the issue manifests:
2025-08-26 22:03:23.280255+0100 0x3b2c00 Default 0x0 0 0 kernel: cfil_hash_entry_log:6088 <CFIL: Error: sosend_reinject() failed>: [4628 java] <UDP(17) in so 9e934ceda1c13379 50826943645358435 50826943645358435 ag>
2025-08-26 22:03:23.280267+0100 0x3b2c00 Default 0x0 0 0 kernel: cfil_service_inject_queue:4472 CFIL: sosend() failed 22
The issue seems to be rooted in the built-in Application Firewall, as disabling it “fixes” the issue. It doesn’t seem to matter that the process is on the “allow” list.
We’re using Gradle 7.6.4, 8.0.2 and 8.14.1 in various repositories, so the version doesn’t seem to matter, neither does which repo we use.
The most reliable way to reproduce is to run two Gradle builds at the same time or very quickly after each other.
We would really appreciate a fix for this as it really negatively affects the developer experience. I've raised FB19916240 for this.
Many thanks,
Hi,
I played around the last days with the new NetworkConnection API from Network framework that supports structured concurrency. I discovered a behavior, which is unexpected from my understanding.
Let's say you have a dead endpoint or something that does not exist. Something where you receive a noSuchRecord error. When I then try to send data, I would expect that the send function throws an error but this does not happen. The function now suspends indefinitely which is well not a great behavior.
Example simplified:
func send() async {
let connection = NetworkConnection(to: .hostPort(host: "apple.co.com", port: 8080)) {
TCP()
}
do {
try await connection.send("Hello World!".raw)
} catch {
print(error)
}
}
I'm not sure if this is the intended behavior or how this should be handled.
Thanks and best regards,
Vinz
I've experimentally seen that the notifications(named:) API of NotificationCenter appears to buffer observed notifications internally. In local testing it appears to be limited to 8 messages. I've been unable to find any documentation of this fact, and the behavior seems like it could lead to software bugs if code is not expecting notifications to potentially be dropped. Is this behavior expected and documented somewhere?
Here is a sample program demonstrating the behavioral difference between the Combine and AsyncSequence-based notification observations:
@Test
nonisolated func testNotificationRace() async throws {
let testName = Notification.Name("TestNotification")
let notificationCount = 100
var observedAsyncIDs = [Int]()
var observedCombineIDs = [Int]()
let subscribe = Task { @MainActor in
print("setting up observer...")
let token = NotificationCenter.default.publisher(for: testName)
.sink { value in
let id = value.userInfo?["id"] as! Int
observedCombineIDs.append(id)
print("🚜 observed note with id: \(id)")
}
defer { extendLifetime(token) }
for await note in NotificationCenter.default.notifications(named: testName) {
let id: Int = note.userInfo?["id"] as! Int
print("🚰 observed note with id: \(id)")
observedAsyncIDs.append(id)
if id == notificationCount { break }
}
}
let post = Task { @MainActor in
for i in 1...notificationCount {
NotificationCenter.default.post(
name: testName,
object: nil,
userInfo: ["id": i]
)
}
}
_ = await (post.value, subscribe.value)
#expect(observedAsyncIDs.count == notificationCount) // 🛑 Expectation failed: (observedAsyncIDs.count → 8) == (notificationCount → 100)
#expect(observedCombineIDs == Array(1...notificationCount))
print("done")
}
I have been using the SCNetworkReachabilityGetFlags for 10+ years to inform users that their request won't work. In my experience this works pretty well although i am aware of the limitations.
Now, i am looking into the NWPathMonitor, and i have one situation that i'm trying to. get my head around - it's asynchronous.
Specifically, i am wondering what to do when my geofences trigger and i want to check network connectivity - i want to tell the user why the operation i'll perform because of the trigger couldn't be done.
SO. say i start a NWPathMonitor in didFinishLaunchingWithOptions. When the app is booted up because of a geofence trigger, might i not end up in a case where my didEnterRegion / didExitRegion gets called before the NWPathMonitor has gotten its first status?
The advantage here with SCNetworkReachabilityGetFlags, as i understand it, would be that it's synchronous?
If i want to upgrade to nwpathmonitor, i guess i have to do a method that creates a nwpathmonitor, uses a semaphore to wait for the first callback, then contunues?
Thoughts appreciated
As per the US state law including SB2420 in Texas.
We are suppose to meet their compliance.
We have following queries
Could you please confirm whether the provided Declared Age Range API framework is available for sandbox testing
How does the API respond for a region other than Texas
Hi all,
We are migrating a SCSI HBA driver from KEXT to DriverKit (DEXT), with our DEXT inheriting from IOUserSCSIParallelInterfaceController. We've encountered a data corruption issue that is reliably reproducible under specific conditions and are hoping for some assistance from the community.
Hardware and Driver Configuration:
Controller: LSI 3108
DEXT Configuration: We are reporting our hardware limitations to the framework via the UserReportHBAConstraints function, with the following key settings:
// UserReportHBAConstraints...
addConstraint(kIOMaximumSegmentAddressableBitCountKey, 0x20); // 32-bit
addConstraint(kIOMaximumSegmentCountWriteKey, 129);
addConstraint(kIOMaximumByteCountWriteKey, 0x80000); // 512KB
Observed Behavior: Direct I/O vs. Buffered I/O
We've observed that the I/O behavior differs drastically depending on whether it goes through the system file cache:
1. Direct I/O (Bypassing System Cache) -> 100% Successful
When we use fio with the direct=1 flag, our read/write and data verification tests pass perfectly for all file sizes, including 20GB+.
2. Buffered I/O (Using System Cache) -> 100% Failure at >128MB
Whether we use the standard cp command or fio with the direct=1 option removed to simulate buffered I/O, we observe the exact same, clear failure threshold:
Test Results:
File sizes ≤ 128MB: Success. Data checksums match perfectly.
File sizes ≥ 256MB: Failure. Checksums do not match, and the destination file is corrupted.
Evidence of failure reproduced with fio (buffered_integrity_test.fio, with direct=1 removed):
fio --size=128M buffered_integrity_test.fio -> Test Succeeded (err=0).
fio --size=256M buffered_integrity_test.fio -> Test Failed (err=92), reporting the following error, which proves a data mismatch during the verification phase:
verify: bad header ... at file ... offset 1048576, length 1048576
fio: ... error=Illegal byte sequence
Our Analysis and Hypothesis
The phenomenon of "Direct I/O succeeding while Buffered I/O fails" suggests the problem may be related to the cache synchronization mechanism at the end of the I/O process:
Our UserProcessParallelTask_Impl function correctly handles READ and WRITE commands.
When cp or fio (buffered) runs, the WRITE commands are successfully written to the LSI 3108 controller's onboard DRAM cache, and success is reported up the stack.
At the end of the operation, to ensure data is flushed to disk, the macOS file system issues an fsync, which is ultimately translated into a SYNCHRONIZE CACHE SCSI command (Opcode 0x35 or 0x91) and sent to our UserProcessParallelTask_Impl.
We hypothesize that our code may not be correctly identifying or handling this SYNCHRONIZE CACHE opcode. It might be reporting "success" up the stack without actually commanding the hardware to flush its cache to the physical disk.
The OS receives this "success" status and assumes the operation is safely complete.
In reality, however, the last batch of data remains only in the controller's volatile DRAM cache and is eventually lost.
This results in an incomplete or incorrect file tail, and while the file size may be correct, the data checksum will inevitably fail.
Summary
Our DEXT driver performs correctly when handling Direct I/O but consistently fails with data corruption when handling Buffered I/O for files larger than 128MB. We can reliably reproduce this issue using fio with the direct=1 option removed.
The root cause is very likely the improper handling of the SYNCHRONIZE CACHE command within our UserProcessParallelTask. P.S. This issue did not exist in the original KEXT version of the driver.
We would appreciate any advice or guidance on this issue.
Thank you.
Hi all,
I have a working macOS (Intel) system extension app that currently uses only a Content Filter (NEFilterDataProvider). I need to capture/log HTTP and HTTPS traffic in plain text, and I understand NETransparentProxyProvider is the right extension type for that.
For HTTPS I will need TLS inspection / a MITM proxy — I’m new to that and unsure how complex it will be.
For DNS data (in plain text), can I use the same extension, or do I need a separate extension type such as NEPacketTunnelProvider, NEFilterPacketProvider, or NEDNSProxyProvider?
Current architecture:
Two Xcode targets: MainApp and a SystemExtension target.
The SystemExtension target contains multiple network extension types.
MainApp ↔ SystemExtension communicate via a bidirectional NSXPC connection.
I can already enable two extensions (Content Filter and TransparentProxy). With the NETransparentProxy, I still need to implement HTTPS capture.
Questions I’d appreciate help with:
Can NETransparentProxy capture the DNS fields I need (dns_hostname, dns_query_type, dns_response_code, dns_answer_number, etc.), or do I need an additional extension type to capture DNS in plain text?
If a separate extension is required, is it possible or problematic to include that extension type (Packet Tunnel / DNS Proxy / etc.) in the same SystemExtension Xcode target as the TransparentProxy?
Any recommended resources or guidance on TLS inspection / MITM proxy setup for capturing HTTPS logs?
There are multiple DNS transport types — am I correct that capturing DNS over UDP (port 53) is not necessarily sufficient? Which DNS types should I plan to handle?
I’ve read that TransparentProxy and other extension types (e.g., Packet Tunnel) cannot coexist in the same Xcode target. Is that true?
Best approach for delivering logs from multiple extensions to the main app (is it feasible)? Or what’s the best way to capture logs so an external/independent process (or C/C++ daemon) can consume them?
Required data to capture (not limited to):
All HTTP/HTTPS (request, body, URL, response, etc.)
DNS fields: dns_hostname, dns_query_type, dns_response_code, dns_answer_number, and other DNS data — all in plain text.
I’ve read various resources but remain unclear which extension(s) to use and whether multiple extension types can be combined in one Xcode target. Please ask if you need more details.
Thank you.
Topic:
App & System Services
SubTopic:
Networking
Tags:
Swift
Frameworks
Network Extension
System Extensions
I've noticed that if a call to startVPNTunnel() is made while no network interface is active on the system, the call "succeeds" (i.e., doesn't throw), but the VPN connection state goes straight from NEVPNStatus.disconnecting to NEVPNStatus.disconnected.
The docs for startVPNTunnel() state:
In Swift, this method returns Void and is marked with the throws keyword to indicate that it throws an error in cases of failure.
Additionally, there is an NEVPNConnectionError enum that contains a noNetworkAvailable case. However, this isn't thrown in this case, when startVPNTunnel() is called.
I just wanted to ask under what circumstances startVPNTunnel() does throw, and should this be one of them?
Additionally, to catch such errors, would it be better to call fetchLastDisconnectError() in the .NEVPNStatusDidChange handler?
I am working on a game app that uses multicast to advertise and locate a hosted game. It therefore requires local network permission. Entitlements are set up correctly in the app for using multicast.
I have two iPhones I use for testing my app, one was using iOS 18.1.x (not exactly sure about the value of x) and the other was using a later version of 18. I install the app on these devices via TestFlight. On the device using iOS 18.1.x the networking worked perfectly, and I could host or join a game (connecting to the same app running on my mac).
The device using the later version would fail to see advertised games as well as failing to advertise games itself. Updating this device to the latest iOS (18.7.2) did not fix the problem. To test that it is likely related to the later version of iOS, I updated the 18.1.x device that was working to 18.7.2, and now it does not work either.
I could see the app listed under "Privacy and Security/Local Network" in settings on the device that used to work with 18.1.x, but not the device with the later version of iOS . Now it does not show up on either device. Nor does either device ask for permission when I activate a network game on the app, even after repeatedly deleting and reinstalling the app as well as resetting the devices.
Why does the device not ask for permission when I try to set up a network game, and how can I get this working again?
Topic:
App & System Services
SubTopic:
Networking
I've developed a custom FSKit module which all seems to work OK when tested with a dummy test FSResource.
However, the end goal is to mount a user's entire home directory via my FSKit module. I obviously need the mount to occur before that user logs in for the first time, since that is when their home directory will be first populated. I also have to perform the mount as the user in question since their home directory must be owned by them.
I have written a LaunchDaemon module to call /sbin/mount, running as that user, however I notice that whether FSKit modules are enabled or disabled seems to be a per-user setting. If I enable my FSKit module as User A in 'System Settings > Login Items and Extensions' and then log in as User B and go into Login Items and Extensions, the module shows as disabled.
Is there a way to enable my FSKit module globally so that it is enabled for all users without each user having to go into System Settings and manually enable it? Or a way of enabling it via command line that I can run with "sudo -u" ? And would this enable me to mount a filesystem before any user has logged in?
I want to implement a feature on macOS using FileProvider: only grant the allowsTrashing permission to files that have already been downloaded, while not granting it to dataless files. However, the system will automatically drain and clear the content. How can this be detected (and how to determine whether a file is dataless)