I'm looking into a newer XPC API available starting with macOS 14. Although it's declared as a low-level API I can't figure it how to specify code signing requirement using XPCListener and XPCSession. How do I connect it with xpc_listener_set_peer_code_signing_requirement and xpc_connection_set_peer_code_signing_requirement which require xpc_listener_t and xpc_connection_t respectively?
Foundation XPC is declared as a high-level API and provides easy ways to specify code signing requirements on both ends of xpc.
I'm confused with all these XPC APIs and their future:
Newer really high-level XPCListener and XPCSession API (in low-level framework???)
Low-level xpc_listener_t & xpc_connection_t -like API. Is it being replaced by newer XPCListener and XPCSession?
How is it related to High-level Foundation XPC? Are NSXPCListener and NSXPCConnection going to be deprecated and replaced by XPCListener and XPCSession??
Processes & Concurrency
RSS for tagDiscover how the operating system manages multiple applications and processes simultaneously, ensuring smooth multitasking performance.
Post
Replies
Boosts
Views
Activity
I need to check how long we can run an app in background which act as a consumer of data / control filter. What is the max time we get to run our app in background without any of these app strategies?
https://developer.apple.com/documentation/backgroundtasks/choosing-background-strategies-for-your-app
I am encountering an issue when making an API call using URLSession with DispatchQueue.global(qos: .background).async on a real device running tvOS 18. The code works as expected on tvOS 17 and in the simulator for tvOS 18, but when I remove the debug mode, After the API call it takes few mintues or 5 to 10 min to load the data on the real device.
Code: Here’s the code I am using for the API call:
appconfig.getFeedURLData(feedUrl: feedUrl, timeOut: kRequestTimeOut, apiMethod: ApiMethod.POST.rawValue) { (result) in
self.EpisodeItems = Utilities.sharedInstance.getEpisodeArray(data: result)
}
func getFeedURLData(feedUrl: String, timeOut: Int, apiMethod: String, completion: @escaping (_ result: Data?) -> ()) {
guard let validUrl = URL(string: feedUrl) else { return }
var request = URLRequest(url: validUrl, cachePolicy: .useProtocolCachePolicy, timeoutInterval: TimeInterval(timeOut))
let userPasswordString = "\(KappSecret):\(KappPassword)"
let userPasswordData = userPasswordString.data(using: .utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: .lineLength64Characters)
let authString = "Basic \(base64EncodedCredential)"
let headers = [
"authorization": authString,
"cache-control": "no-cache",
"user-agent": "TN-CTV-\(kPlateForm)-\(kAppVersion)"
]
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = apiMethod
request.allHTTPHeaderFields = headers
let response = URLSession.requestSynchronousData(request as URLRequest)
if response.1 != nil {
do {
guard let parsedData = try JSONSerialization.jsonObject(with: response.1!, options: .mutableContainers) as? AnyObject else {
print("Error parsing data")
completion(nil)
return
}
print(parsedData)
completion(response.1)
return
} catch let error {
print("Error: \(error.localizedDescription)")
completion(response.1)
return
}
}
completion(response.1)
}
import Foundation
public extension URLSession {
public static func requestSynchronousData(_ request: URLRequest) -> (URLResponse?, Data?) {
var data: Data? = nil
var responseData: URLResponse? = nil
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request) { taskData, response, error in
data = taskData
responseData = response
if data == nil, let error = error {
print(error)
}
semaphore.signal()
}
task.resume()
_ = semaphore.wait(timeout: .distantFuture)
return (responseData, data)
}
public static func requestSynchronousDataWithURLString(_ requestString: String) -> (URLResponse?, Data?) {
guard let url = URL(string: requestString.checkValidUrl()) else { return (nil, nil) }
let request = URLRequest(url: url)
return URLSession.requestSynchronousData(request)
}
}
Issue Description: Working scenario: The API call works fine on tvOS 17 and in the simulator for tvOS 18. Problem: When running on a real device with tvOS 18, the API call takes time[enter image description here] when debug mode is disabled, but works fine when debug mode is enabled, Data is loading after few minutes.
Error message: Error Domain=WKErrorDomain Code=11 "Timed out while loading attributed string content" UserInfo={NSLocalizedDescription=Timed out while loading attributed string content} NSURLConnection finished with error - code -1001 nw_read_request_report [C4] Receive failed with error "Socket is not connected" Snapshot request 0x30089b3c0 complete with error: <NSError: 0x3009373f0; domain: BSActionErrorDomain; code: 1 ("response-not-possible")> tcp_input [C7.1.1.1:3] flags=[R] seq=817957096, ack=0, win=0 state=CLOSE_WAIT rcv_nxt=817957096, snd_una=275546887
Environment: Xcode version: 16.1 Real device: Model A1625 (32GB) tvOS version: 18.1
Debugging steps I’ve taken: I’ve verified that the issue does not occur in debug mode. I’ve confirmed that the API call works fine on tvOS 17 and in the simulator (tvOS 18). The error suggests a network timeout (-1001) and a socket connection issue ("Socket is not connected").
Questions:
Is this a known issue with tvOS 18 on real devices? Are there any specific settings or configurations in tvOS 18 that could be causing the timeout error in non-debug mode? Could this be related to how URLSession or networking behaves differently in release mode? I would appreciate any help or insights into this issue!
Hi,
we are in the process of exploring how to create an installer for our array of apps.
We have come to the conclusion that regular .pkg installers produced by pkgbuild and productbuild are unfulfilling of our expectations. [1]
Regardless, our installer needs to place files at privileged locations (/Library/Application Support) so we are looking into how to best solve this problem, with the user having
the largest clarity on what they are about to do (so no shady "wants to make changes" dialogs)
the least steps to do to install these files in the right place (so no targeted NSSavePanel-s)
Now, we have done our light reading via some nicely collected posts on the topic (https://forums.developer.apple.com/forums/thread/708765 for example) and the single missing option in the list of privilege escalation models seems to be a one-time privilege escalation from a GUI app.
Our reasons for declaring so:
AuthorizationExecuteWithPrivileges is long deprecated and we are trying to build a futureproof solution
NSAppleScript is just putting up a shady ("wants to make changes") dialog when trying something like this:
$ osascript -e "set filePath to \"/Library/Application Support\"" -e "do shell script \"touch \" & the quoted form of filePath & \"/yyy.txt\" with administrator privileges"
Is there another way to request a one-time authorization from the admin to perform such a simple operation as copying a file to a protected location?
I know it's possible to externalize and internalize Authorization Rights, but they are just an interface to create extra rights and use them as barriers, because they don't actually pass the required right to further operations based on this documentation.
Using SMAppService to register a daemon, which has to be manually allowed by the user adds a lot to the complexity of this installation process, and is something we would like to avoid if possible. (And it's also not the right security model if we want to be honest - we don't want ongoing administrator rights and a daemon)
Is there something we haven't taken into consideration?
[1] preinstall scripts run after the choices are presented during installation and we would need advanced logic (not the limited JavaScript system/files API provided by Installer JS) - plus, the GUI is obviously very limited in a .pkg :(
I have a process [command line cpp application] which i want to run always such as it should relaunch after a crash, after device startup etc.
I created a launchd Property List File with KeepAlive true and placed under /Library/LaunchDaemons.
Problem Statements:
I have a bash script to start and stop this process.
start using: launchctl bootstrap.
stop involve these two steps:
send SIGTERM signal and wait untill process stops after doing some cleanups
launchctl bootout [It doesn't sends SIGTERM]
during steps 1 - Process is getting stop, but also getting immediate relaunch by launchctl
during step 2 - it getting stop again.
is there a proper way so that we can disable KeepAlive temporarily so that process will not launch during step 1?
or suggest other ways to handle this?
Hello everyone!
I'm having a problem with background tasks running in the foreground.
When a user enters the app, a background task is triggered. I've written some code to check if the app is in the foreground and to prevent the task from running, but it doesn't always work. Sometimes the task runs in the background as expected, but other times it runs in the foreground, as I mentioned earlier.
Could it be that I'm doing something wrong? Any suggestions would be appreciated.
here is code:
class BackgroundTaskService {
@Environment(\.scenePhase) var scenePhase
static let shared = BackgroundTaskService()
private init() {}
// MARK: - create task
func createCheckTask() {
let identifier = TaskIdentifier.check
BGTaskScheduler.shared.getPendingTaskRequests { requests in
if requests.contains(where: { $0.identifier == identifier.rawValue }) {
return
}
self.createByInterval(identifier: identifier.rawValue, interval: identifier.interval)
}
}
private func createByInterval(identifier: String, interval: TimeInterval) {
let request = BGProcessingTaskRequest(identifier: identifier)
request.earliestBeginDate = Date(timeIntervalSinceNow: interval)
scheduleTask(request: request)
}
// MARK: submit task
private func scheduleTask(request: BGProcessingTaskRequest) {
do {
try BGTaskScheduler.shared.submit(request)
} catch {
// some actions with error
}
}
// MARK: background actions
func checkTask(task: BGProcessingTask) {
let today = Calendar.current.startOfDay(for: Date())
let lastExecutionDate = UserDefaults.standard.object(forKey: "lastCheckExecutionDate") as? Date ?? Date.distantPast
let notRunnedToday = !Calendar.current.isDate(today, inSameDayAs: lastExecutionDate)
guard notRunnedToday else {
task.setTaskCompleted(success: true)
createCheckTask()
return
}
if scenePhase == .background {
TaskActionStore.shared.getAction(for: task.identifier)?()
}
task.setTaskCompleted(success: true)
UserDefaults.standard.set(today, forKey: "lastCheckExecutionDate")
createCheckTask()
}
}
And in AppDelegate:
BGTaskScheduler.shared.register(forTaskWithIdentifier: "check", using: nil) { task in
guard let task = task as? BGProcessingTask else { return }
BackgroundTaskService.shared.checkNodeTask(task: task)
}
BackgroundTaskService.shared.createCheckTask()
I was experimenting with Service Management API and Xcode project from https://developer.apple.com/documentation/servicemanagement/updating-your-app-package-installer-to-use-the-new-service-management-api
and faced some issues with the API.
I replaced agent with XPC service and tried to re-register it.
Use case is a new app package installation with a newer service binary. In order to get the running service restarted with the new binary it's required to unregister old version and register new one. Otherwise the old version would be still running after app upgrade.
The problem is that register fails with "Operation not permitted" error after running unregister which seems to work fine.
Experiments with some delays (500ms) between unregister and register seem to help but it's a not a good solution to work around the problem.
I'm using open func unregister() async throws with description:
The completion handler will be invoked after the running process has been killed if successful or will be invoked whenever an error occurs. After the completion handler has been invoked it is safe to re-register the service.
Sample output with no 500ms sleep between unregister and register calls:
/Library/Application\ Support/YourDeveloperName/SMAppServiceSampleCode.app/Contents/MacOS/SMAppServiceSampleCode unregister && /Library/Application\ Support/YourDeveloperName/SMAppServiceSampleCode.app/Contents/MacOS/SMAppServiceSampleCode register
Successfully unregistered LaunchDaemon(com.xpc.example.service.plist)
Unable to register LaunchDaemon(com.xpc.example.service.plist): Error Domain=SMAppServiceErrorDomain Code=1 "Operation not permitted" UserInfo={NSLocalizedFailureReason=Operation not permitted}
In fact it doesn't seem to be safe to re-register. Any explanation would much appreciated!
=====================================================
Side issue #2: I tried to add a similar helper executable as in the original project with register/unregister and put it inside the same app bundle but at a different location like Contents/Helpers/ folder instead of Contents/MacOS. And it always fails with this error:
Error Domain=SMAppServiceErrorDomain Code=3 "Codesigning failure loading plist: com.okta.service.osquery code: -67028" UserInfo={NSLocalizedFailureReason=Codesigning failure loading plist: com.okta.service.osquery code: -67028}
When I moved the helper binary to Contents/MacOS/ folder along with the main app executable it starts working fine again. Other folders like Resources/XPCServices also don't work.
Is it a hard requirement for an executable to be located inside main Contents/MacOS folder in order to be able to call SMAppService register/unregister APIs? I haven't found any documentation regarding this requirement.
Thanks,
Pavel
When using conformance to ObservableObject and then doing async work in a Task, you will get a warning courtesy of Combine if you then update an @Published or @State var from anywhere but the main thread. However, if you are using @Observable there is no such warning.
Also, Thread.current is unavailable in asynchronous contexts, so says the warning. And I have read that in a sense you simply aren't concerned with what thread an async task is on.
So for me, that begs a question. Is the lack of a warning, which when using Combine is rather important as ignoring it could lead to crashes, a pretty major bug that Apple seemingly should have addressed long ago? Or is it just not an issue to update state from another thread, because Xcode is doing that work for us behind the scenes too, just as it manages what thread the async task is running on when we don't specify?
I see a lot of posts about this from around the initial release of Async/Await talking about using await MainActor.run {} at the point the state variable is updated, usually also complaining about the lack of a warning. But ow years later there is still no warning and I have to wonder if this is actually a non issue. On some ways similar to the fact that many of the early posts I have seen related to @Observable have examples of an @Observable ViewModel instantiated in the view as an @State variable, but in fact this is not needed as that is addressed behind the scenes for all properties of an @Observable type.
At least, that is my understanding now, but I am learning Swift coming from a PowerShell background so I question my understanding a lot.
I updated my computer to Sonoma, and now my LaunchDaemon will not load.
I have the following setup :
File in /Library/LaunchDaemons/com.startup.plist
like this :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.startup</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/bash</string>
<string>/Library/Scripts/Startup/startup.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/com.startup.stderr</string>
<key>StandardOutPath</key>
<string>/tmp/com.startup.stdout</string>
</dict>
</plist>
File in File in /Library/Scripts/Startup/startup.sh
#!/bin/zsh
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users:/Users/root:/Users/root/Scripts:/Library/Scripts:/Library/Scripts/Startup
#Load modules for Fuse
/Library/Filesystems/macfuse.fs/Contents/Resources/load_macfuse
/usr/sbin/sysctl -w vfs.generic.macfuse.tunables.allow_other=1
#Connect to XXXXXX_net
/bin/sleep 28
myip=0
while [ $myip = 0 ]
do
/bin/sleep 3
myip=$(ifconfig -l | xargs -n1 ipconfig getifaddr)
done
/usr/local/bin/sshfs XXXX@XXXXXX.net: /Volumes/XXXXXX.net -o local,auto_cache,reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,ConnectTimeout=5,daemon_timeout=60,iosize=2097152,volname=XXXXXX.net,allow_other,defer_permissions,async_read,Ciphers=aes128-gcm@openssh.com,Cipher=aes128-gcm@openssh.com,compression=no
And then we need some commands to be run as root user during boot :
/private/etc/sudoers.d/startup-script-nopasswd
username ALL = (root) NOPASSWD: /usr/sbin/sysctl
username ALL = (root) NOPASSWD: /usr/local/bin/sshfs
As of now, I cant even get the /Library/LaunchDaemons/com.startup.plist
to run after i updated the macOS to Sonoma ….
Hi, I want to create a custom application launcher, so I'd like the app to be able to just list the apps installed and launch them when touched. My idea is to have a Minimalist UI in order to enhance productivity.
Is it possible? I see there is already one App doing it
https://apps.apple.com/us/app/dumb-phone/id6504743503
I want to do something similar, so how does the App in the link obtains the Apps installed on the device?
I created a macOS app, added an XPC service target, and also added a source editor extension.
in The source editor extension‘s perform function. It doesn't work
- (void)performCommandWithInvocation:(XCSourceEditorCommandInvocation *)invocation completionHandler:(void (^)(NSError * _Nullable nilOrError))completionHandler {
self.xpcConnect = [[NSXPCConnection alloc] initWithServiceName:@"test.TestNewXPCApp.NewXPC"];
NSXPCInterface *interface = [NSXPCInterface interfaceWithProtocol:@protocol(NewXPCProtocol)];
self.xpcConnect.remoteObjectInterface = interface;
[self.xpcConnect resume];
[[self.xpcConnect remoteObjectProxy] performCalculationWithNumber:@231 andNumber:@119 withReply:^(NSNumber *reply) {
// We have received a response.
NSLog(@"ui success%@", reply);
}];
But In ViewControler.m, executing the same code , it can work.
So why is it possible to connect to the XPC service from within the macOS app, but not from the source editor extension?
Let's say I queue some tasks on DispatchQueue.global() and then switch to another app or locking screen for a while. The app was not terminated but stayed in the background.
Is there a chance that some tasks queued but not yet start could be discarded, even if the app hasn’t been terminated, after switching to another app or locking the screen for a while?
我这边用了几台机器升级iOS 18.1并没有测试出来问题,但是审核员测试出来了问题,并将崩溃报告发给了我。
以下是审核员发给我的的测试环境及崩溃报告:
Device type: iPad Air (5th generation)
OS version: iOS 18.1
崩溃报告如下:
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x196ae7c38 objc_msgSend + 56
1 UIKitCore 0x19bf9c0f4 -[UIView bounds] + 32
2 UIKitCore 0x19c14e15c -[UIScrollView _didEndDirectManipulationWithScrubbingDirection:] + 108
3 UIKitCore 0x19d4cd3e8 -[UIScrollView _stopScrollingNotify:pin:tramplingAnimationDependentFlags:] + 108
4 UIKitCore 0x19d4cd548 -[UIScrollView _stopScrollingAndZoomingAnimationsPinningToContentViewport:tramplingAnimationDependentFlags:] + 52
5 UIKitCore 0x19c385a28 -[UIScrollView dealloc] + 88
6 libsystem_blocks.dylib 0x221c29860 bool HelperBase::disposeCapture<(HelperBase::BlockCaptureKind)3>(unsigned int, unsigned char*) + 68
7 libsystem_blocks.dylib 0x221c29570 HelperBase::destroyBlock(Block_layout*, bool, unsigned char*) + 160
8 libsystem_blocks.dylib 0x221c29030 _call_dispose_helpers_excp + 72
9 libsystem_blocks.dylib 0x221c28fcc _Block_release + 256
10 libdispatch.dylib 0x1a14fe0d0 _dispatch_client_callout + 20
11 libdispatch.dylib 0x1a150c9e0 _dispatch_main_queue_drain + 980
12 libdispatch.dylib 0x1a150c5fc _dispatch_main_queue_callback_4CF + 44
13 CoreFoundation 0x1997fc204 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE + 16
14 CoreFoundation 0x1997f9440 __CFRunLoopRun + 1996
15 CoreFoundation 0x1997f8830 CFRunLoopRunSpecific + 588
16 GraphicsServices 0x1e57d81c4 GSEventRunModal + 164
17 UIKitCore 0x19c35eeb0 -[UIApplication _run] + 816
18 UIKitCore 0x19c40d5b4 UIApplicationMain + 340
19 BXT 0x104d90090 0x104aa4000 + 3063952
20 dyld 0x1bf1e6ec8 start + 2724
Thread 1:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 2:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 3:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 4 name: com.apple.uikit.eventfetch-thread
Thread 4:
0 libsystem_kernel.dylib 0x1e9bba688 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x1e9bbdd98 mach_msg2_internal + 80
2 libsystem_kernel.dylib 0x1e9bbdcb0 mach_msg_overwrite + 424
3 libsystem_kernel.dylib 0x1e9bbdafc mach_msg + 24
4 CoreFoundation 0x1997f9a84 __CFRunLoopServiceMachPort + 160
5 CoreFoundation 0x1997f9130 __CFRunLoopRun + 1212
6 CoreFoundation 0x1997f8830 CFRunLoopRunSpecific + 588
7 Foundation 0x1984a0500 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212
8 Foundation 0x1984a0350 -[NSRunLoop(NSRunLoop) runUntilDate:] + 64
9 UIKitCore 0x19c372358 -[UIEventFetcher threadMain] + 420
10 Foundation 0x1984b16c8 NSThread__start + 724
11 libsystem_pthread.dylib 0x221c3137c _pthread_start + 136
12 libsystem_pthread.dylib 0x221c2c494 thread_start + 8
Thread 5:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 6:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 7:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 8:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 9:
0 libsystem_pthread.dylib 0x221c2c480 start_wqthread + 0
Thread 10 name: JavaScriptCore libpas scavenger
Thread 10:
0 libsystem_kernel.dylib 0x1e9bbff90 __psynch_cvwait + 8
1 libsystem_pthread.dylib 0x221c2ea50 _pthread_cond_wait + 1204
2 JavaScriptCore 0x1b156aca4 scavenger_thread_main + 1512
3 libsystem_pthread.dylib 0x221c3137c _pthread_start + 136
4 libsystem_pthread.dylib 0x221c2c494 thread_start + 8
Thread 11 name: WebThread
Thread 11:
0 libsystem_kernel.dylib 0x1e9bba688 mach_msg2_trap + 8
1 libsystem_kernel.dylib 0x1e9bbdd98 mach_msg2_internal + 80
2 libsystem_kernel.dylib 0x1e9bbdcb0 mach_msg_overwrite + 424
3 libsystem_kernel.dylib 0x1e9bbdafc mach_msg + 24
4 CoreFoundation 0x1997f9a84 __CFRunLoopServiceMachPort + 160
5 CoreFoundation 0x1997f9130 __CFRunLoopRun + 1212
6 CoreFoundation 0x1997f8830 CFRunLoopRunSpecific + 588
7 WebCore 0x1ad46bb18 RunWebThread(void*) + 780
8 libsystem_pthread.dylib 0x221c3137c _pthread_start + 136
9 libsystem_pthread.dylib 0x221c2c494 thread_start + 8
Thread 0 crashed with ARM Thread State (64-bit):
x0: 0x00000003029b1840 x1: 0x0000000208981838 x2: 0x0000000000000000 x3: 0x0000000000000000
x4: 0x0000000000000001 x5: 0x00000f0300000000 x6: 0x0000000000000002 x7: 0x0000000000000000
x8: 0x00000000000000a0 x9: 0x0000000208981838 x10: 0x00000000024b036c x11: 0x00000000024b036c
x12: 0x0000000000000000 x13: 0x00000000024b036c x14: 0x00000003029b1bc0 x15: 0x00000003029b1bc0
x16: 0x00000003029b1bc0 x17: 0x0000000205f46018 x18: 0x0000000000000000 x19: 0x0000000136841400
x20: 0x00000001fd5f4588 x21: 0xffffffffffffffff x22: 0x00000000000006d8 x23: 0x0000000136841ad8
x24: 0x0000000000000000 x25: 0x00000001fd5969e0 x26: 0x00000003032b7640 x27: 0x000000000000000f
x28: 0x0000000000000000 fp: 0x000000016b35a4e0 lr: 0x000000019bf9c0f4
sp: 0x000000016b35a4e0 pc: 0x0000000196ae7c38 cpsr: 0x20001000
far: 0x00000000024b036c esr: 0x92000006 (Data Abort) byte read Translation fault
Our application has seen a surge in the volume of background launches starting from April and May, and we want to know under what circumstances the application can be launched from the background.
First, here's how I determined background launches: we analyze user logs and append UIApplication.appState to each line of log, finding that every log from the start to the end of user sessions has an appState of UIApplicationStateBackground.
By checking the "ActivePrewarm" in main() and printing the launch options from application:didFinishLaunchingWithOptions:, we found several scenarios for background launches:
launchOptions has a value with the key UIApplicationLaunchOptionsRemoteNotificationKey.
launchOptions has no value and there is no "ActivePrewarm."
launchOptions has no value but has "ActivePrewarm."
I would like to know:
Under what circumstances will notifications trigger a background launch (I cannot replicate this locally)?
Under what circumstances does an application launch in the background and trigger application:didFinishLaunchingWithOptions: but without any launch options?
I hope informations below can provide some insights.
Regarding "ActivePrewarm," I've read various questions and answers in the Apple Developer Forums, such as this thread, which states that "ActivePrewarm" does not trigger application:didFinishLaunchingWithOptions: but occurs due to certain behaviors in the application. I would like to know what behaviors may cause this background launch, as there is no information in the launch options, or how I can identify what behaviors triggered it.
Specifically, based on that same thread, I've tried to gather more information using runningboardd, and I've currently identified two special cases:
When I restart my phone and unlock it after a short period, there is information:
<RBSDomainAttribute| domain:"com.apple.dasd" name:"DYLDLaunch" sourceEnvironment:"(null)">
]>
Every day, at intervals of a few hours, there is information:
<RBSDomainAttribute| domain:"com.apple.dasd" name:"DYLDLaunch" sourceEnvironment:"(null)">
]>
Then, the following similar information follows:
12:15:56.047625+0800 runningboardd Executing launch request for app<{my_bundle_id}((null))> (DAS Prewarm launch)
12:15:56.050311+0800 runningboardd Creating and launching job for: app<{my_bundle_id}((null))>
12:15:56.050333+0800 runningboardd _mutateContextIfNeeded called for {my_bundle_id}
12:15:56.080560+0800 runningboardd app<{my_bundle_id}((null))>: -[RBPersonaManager personaForIdentity:context:personaUID:personaUniqueString:] required 0.000954 ms (wallclock); resolved to {1000, 39E408CF-2E67-4DB0-BF73-CFC5792285CD}
12:15:56.080632+0800 runningboardd 'app<{my_bundle_id}(39E408CF-2E67-4DB0-BF73-CFC5792285CD)>' Skipping container path lookup because containerization was prevented (<RBSLaunchContext: 0xcd8cc9180>)
12:15:56.080939+0800 runningboardd 'app<{my_bundle_id}(39E408CF-2E67-4DB0-BF73-CFC5792285CD)>' Constructed job description:
<dictionary: 0xcd8aa2a00> { count = 19, transaction: 0, voucher = 0x0, contents = *** }
12:15:56.084839+0800 runningboardd [app<{my_bundle_id}((null))>:1649] Memory Limits: active 4096 inactive 4096
<private>
12:15:56.084861+0800 runningboardd [app<{my_bundle_id}((null))>:1649] This process will be managed.
12:15:56.084882+0800 runningboardd Now tracking process: [app<{my_bundle_id}((null))>:1649]
12:15:56.084928+0800 runningboardd Calculated state for app<{my_bundle_id}((null))>: running-active (role: Background) (endowments: (null))
12:15:56.086762+0800 runningboardd Using default underlying assertion for app: [app<{my_bundle_id}((null))>:1649]
12:15:56.086977+0800 runningboardd Acquiring assertion targeting [app<{my_bundle_id}((null))>:1649] from originator [app<{my_bundle_id}((null))>:1649] with description <RBSAssertionDescriptor| "RB Underlying Assertion" ID:33-33-23101 target:1649 attributes:[
<RBSDomainAttribute| domain:"com.apple.underlying" name:"defaultUnderlyingAppAssertion" sourceEnvironment:"(null)">,
<RBSAcquisitionCompletionAttribute| policy:AfterApplication>
]>
12:15:56.087203+0800 runningboardd Assertion 33-33-23101 (target:[app<{my_bundle_id}((null))>:1649]) will be created as active
12:15:56.087946+0800 runningboardd [app<{my_bundle_id}((null))>:1649] reported to RB as running
12:15:56.088053+0800 runningboardd Calculated state for app<{my_bundle_id}((null))>: running-active (role: Background) (endowments: (null))
12:15:56.088114+0800 runningboardd [app<{my_bundle_id}((null))>:1649] Set jetsam priority to 0 [0] flag[1]
12:15:56.088136+0800 runningboardd [app<{my_bundle_id}((null))>:1649] Resuming task.
12:15:56.088211+0800 runningboardd [app<{my_bundle_id}((null))>:1649] Set darwin role to: Background
12:15:56.088449+0800 runningboardd [app<{my_bundle_id}((null))>:1649] set Memory Limits to Hard Inactive (4096)
12:15:56.089314+0800 runningboardd Successfully acquired underlying assertion for [app<{my_bundle_id}((null))>:1649]
12:15:56.589755+0800 runningboardd Invalidating assertion 33-76-23100 (target:app<{my_bundle_id}((null))>) from originator [osservice<com.apple.dasd>:76]
12:15:56.590332+0800 runningboardd Removed last relative-start-date-defining assertion for process app<{my_bundle_id}((null))>
12:15:56.593760+0800 runningboardd [app<{my_bundle_id}((null))>:1649] Suspending task.
12:15:56.594120+0800 runningboardd Calculated state for app<{my_bundle_id}((null))>: running-suspended (role: None) (endowments: (null))
From these logs, I understand that the system is accelerating the launch speed of the application.
But the time interval between these two logs below is very short, which suggests that the prewarm is executed just before main, and then the process is suspended. Is this understanding correct?
12:15:56.089314+0800 runningboardd Successfully acquired underlying assertion ...
12:15:56.589755+0800 runningboardd Invalidating assertion ...
Regarding "DAS DYLD3 Closure Generation," I speculate that after a user restarts their phone, the system uses DYLD3 to prepare closures for frequently used applications, allowing for faster application launches. Is this assumption correct?
On iPhone, we can use iBeacon to wake up the APP in the background for Bluetooth scanning connection, now we want to port the function to AppleWatch APP, but the API related to iBeacon is not applicable on watchOS, does watchOS have a similar wake up mechanism?
I'm calling the following function in a SwiftUI View modifier in Xcode 16.1:
nonisolated function f -> CGFloat {
let semaphore = DispatchSemaphore(value: 0)
var a: CGFloat = 0
DispatchQueue.main.async {
a = ...
semaphore.signal()
}
semaphore.wait()
return a
}
The app freezes, and code in the main queue is never executed.
Greetings, does anyone know if can I use background capabilities to refresh auth token safely without be aware of the OS kill this task?
Is there any Apple Oficial recommended flow to handle that?
thanks
Per Quinn the Eskimo's great summary post I am looking for the WWDC 2020 Session 10063 "Background execution demystified" but all links seem to be broken or redirecting elsewhere. Does anyone know where one could find a reputable copy of the video to watch as we explore background execution?
I am developing an application usinh native apps, where the app needs to continuously sync data (such as daily tasks and orders) even when offline or running in the background. However, on iOS, the background sync stops after 30 seconds, limiting the functionality. The Background Sync API and Service Workers seem restricted on iOS, causing syncing to fail when the app is in the background or offline. What is the best way to ensure continuous background synchronization on iOS? Additionally, what is the most efficient data storage approach for managing offline capabilities and syncing smoothly when the network is unstable and for the background sync?
Hello I'm a beginner to Swift Concurrency and have run into an issue with AsyncStream. I've run into a situation that causes an observing of a for loop to receiving a values from an AsyncStream.
At the bottom is the code that you can copy it into a Swift Playground and run.
The code is supposed to mock a system that has a service going through a filter to read and write to a connection.
Here is a log of the prints
🙈🫴 setupRTFAsyncWrites Start
⬅️ Pretend to write 0
➡️ Pretend to read 0
feed into filter 0
yield write data 1
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
⬅️ Pretend to write 1
➡️ Pretend to read 1
feed into filter 1
yield write data 2
// here our for loop should have picked up the value sent down the continuation. But instead it just sits here.
Sample that can go into a playground
//: A UIKit based Playground for presenting user interface
import SwiftUI
import PlaygroundSupport
import Combine
import CommonCrypto
import Foundation
class TestConnection {
var didRead: ((Data) -> ()) = { _ in }
var count = 0
init() {
}
func write(data: Data) {
// pretend we sent this to the BT device
print("⬅️ Pretend to write \(count)")
Task {
try await Task.sleep(ms: 200)
print("➡️ Pretend to read \(self.count)")
self.count += 1
// pretend this is a response from the device
self.didRead(Data([0x00]))
}
}
}
enum TestEvent: Sendable {
/// ask some one to write this to the device
case write(Data)
/// the filter is done
case handshakeDone
}
class TestFilter {
var eventsStream: AsyncStream<TestEvent>
var continuation: AsyncStream<TestEvent>.Continuation
private var count = 0
init() {
(self.eventsStream, self.continuation) = AsyncStream<TestEvent>.makeStream(bufferingPolicy: .unbounded)
}
func feed(data: Data) {
print("\tfeed into filter \(count)")
count += 1
if count > 5 {
print("\t✅ handshake done")
self.continuation.yield(.handshakeDone)
return
}
Task {
// data delivered to us by a bluetooth device
// pretend it takes time to process this and then we return with a request to write back to the connection
try await Task.sleep(ms: 200)
print("\tyield write data \(self.count)")
// pretend this is a response from the device
let result = self.continuation.yield(.write(Data([0x11])))
}
}
/// gives the first request to fire to the device for the handshaking sequence
func start() -> Data {
return Data([0x00])
}
}
// Here we facilitate communication between the filter and the connection
class TestService {
private let filter: TestFilter
var task: Task<(), Never>?
let testConn: TestConnection
init(filter: TestFilter) {
self.filter = filter
self.testConn = TestConnection()
self.testConn.didRead = { [weak self] data in
self?.filter.feed(data: data)
}
self.task = Task { [weak self] () in
await self?.setupAsyncWrites()
}
}
func setupAsyncWrites() async {
print("🙈🫴 setupRTFAsyncWrites Start")
for await event in self.filter.eventsStream {
print("\t\t🙈🫴 setupRTFAsyncWrites: \(event)")
guard case .write(let data) = event else {
print("\t\t🙈🫴 NOT data to device: \(event)")
continue
}
print("\t\t⬅️🙈🫴 Async Event: dataToDevice: \(data)")
self.testConn.write(data: data)
} // for
// This shouldn't end
assertionFailure("This should not end")
}
public func handshake() async {
let data = self.filter.start()
self.testConn.write(data: data)
await self.waitForHandshakedone()
}
private func waitForHandshakedone() async {
for await event in self.filter.eventsStream {
if case .handshakeDone = event {
break
}
continue
}
}
}
Task {
let service = TestService(filter: TestFilter())
await service.handshake()
print("Done")
}
/*
This is what happens:
🙈🫴 setupRTFAsyncWrites Start
⬅️ Pretend to write 0
➡️ Pretend to read 0
feed into filter 0
yield write data 1
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
⬅️ Pretend to write 1
➡️ Pretend to read 1
feed into filter 1
yield write data 2
// It just stops here, the `for` loop in setupAsyncWrites() should have picked up the event sent down the continuation after "yield write data 2"
// It should say
🙈🫴 setupRTFAsyncWrites: write(1 bytes)
⬅️🙈🫴 Async Event: dataToDevice: 1 bytes
*/
extension Task<Never, Never> {
public static func sleep(ms duration: UInt64) async throws {
try await Task.sleep(nanoseconds: 1_000_000 * duration)
}
}