Post

Replies

Boosts

Views

Activity

Swift Exception Handling in Apple OSes
I have an app whose logic is in C++ and rest of the parts (UI) are in Swift and SwiftUI. Exceptions can occur in C++ and Swift. I've got the C++ part covered by using the Linux's signal handler mechanism to trap signals which get raised due to exceptions. But how should I capture exceptions in Swift? When I say exceptions in Swift, I mean, divide by zero, force unwrapping of an optional containing nil, out of index access in an array, etc. Basically, anything that can go wrong, I don't want my app to abruptly crash... I need a chance to finalise my stuff, alert the user, prepare diagnostic reports and terminate. I'm looking for a 'catch-all' exception handler. As an example, let's take Android. In Android, there is the setDefaultUncaughtExceptionHandler method to register for all kinds of exceptions in any thread in Kotlin. I'm looking for something similar in Swift that should work for macOS, iOS & iPadOS, tvOS and watchOS. I first came across the NSSetUncaughtExceptionHandler method. My understanding is, this only works when I explicitly raise NSExceptions. When I tested it, observed that the exception handler didn't get invoked for either case - divide by zero or invoking raise. class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { Log("AppDelegate.applicationDidFinishLaunching(_:)") // Set the 'catch-all' exception handler for Swift exceptions. Log("Registering exception handler using NSSetUncaughtExceptionHandler()...") NSSetUncaughtExceptionHandler { (exception: NSException) in Log("AppDelegate.NSUncaughtExceptionHandler()") Log("Exception: \(exception)") } Log("Registering exception handler using NSSetUncaughtExceptionHandler() succeeded!") // For C++, use the Linux's signal mechanism. ExceptionHandlingCpp.RegisterSignals() //ExceptionHandlingCpp.TestExceptionHandler() AppDelegate.TestExceptionHandlerSwift() } static func TestExceptionHandlerSwift() { Log("AppDelegate.TestExceptionHandlerSwift()") DivisionByZero(0) } private static func DivisionByZero(_ divisor: Int) { Log("AppDelegate.DivisionByZero()") let num1: Int = 2 Log("Raising Exception...") //let result: Int = num1/divisor let exception: NSException = NSException(name: NSExceptionName(rawValue: "arbitrary"), reason: "arbitrary reason", userInfo: nil) exception.raise() Log("Returning from DivisionByZero()") } } In the above code, dividing by zero, nor raising a NSException invokes the closure passed to NSSetUncaughtExceptionHandler, evident from the following output logs AppDelegate.applicationWillFinishLaunching(_:) AppDelegate.applicationDidFinishLaunching(_:) Registering exception handler using NSSetUncaughtExceptionHandler()... Registering exception handler using NSSetUncaughtExceptionHandler() succeeded! ExceptionHandlingCpp::RegisterSignals() .... AppDelegate.TestExceptionHandlerSwift() AppDelegate.DivisionByZero() Raising Exception... Currently, I'm reading about ExceptionHandling framework, but this is valid only for macOS. What is the recommended way to capture runtime issues in Swift?
7
0
1k
Nov ’24
Huge timeout values from a failed DiskIO call
I have created a sample app which read/write from a network file. When the file was attempted to open (using open Linux API), connection to network file was lost. The thread which was stuck on the open method, returns after a long time. It was observed that for macOS, the maximum return time of the thread was around 10 mins, whereas in Windows and Linux, the maximum timeout was 60 sec and 90 sec. macOS has a very large timeout before returning the thread with a network failure error. Is this by designed and expected? With a large timeout as 10mins, it's difficult to respond swiftly back to the user.
7
0
243
Sep ’25
SIGFPE not raised when dividing by zero
I have an app whose logic is in C++ and rest of the parts (UI) are in Swift and SwiftUI. When an exception is raised by some C++ code, I'm using the Linux signal handler mechanism to trap it. From my previous post, I understand that fatal exceptions like SIGSEGV, SIGBUS, SIGFPE etc., there's nothing much that can be done by the process. My only intent for using a signal handler is to log something, so that it becomes easy to fix during development. Ofc, even that logging can fail, based on the severity of the exception, but that's okay... make an attempt to log - if it works, great, else the process can terminate. I'm registering for SIGSEGV and SIGFPE with the following code // ExceptionHandlingCpp.hpp file struct tSignals { SignalHandlerFunc signalHandlerFunc; uint32_t signal; [[maybe_unused]] uint8_t reserved[4]; }; // ExceptionHandlingCpp.cpp file tSignals ExceptionHandlingCpp::unixSignals[] = { {HandleSignals, SIGFPE, {0}}, {HandleSignals, SIGSEGV, {0}}, {HandleSignals, SIGKILL, {0}}, }; std::string ExceptionHandlingCpp::signalToString(int signal) { switch(signal) { case SIGFPE: return "SIGFPE"; case SIGSEGV: return "SIGSEGV"; case SIGKILL: return "SIGKILL"; default: return "Unknown signal"; } } void ExceptionHandlingCpp::RegisterSignals() { LOG("ExceptionHandlingCpp::RegisterSignals()"); struct sigaction sa; sa.sa_flags = SA_SIGINFO; for(int i = 0; i < sizeof(unixSignals)/sizeof(tSignals); ++i) { sa.sa_sigaction = unixSignals[i].signalHandlerFunc; if(sigaction(unixSignals[i].signal, &sa, nullptr) == 1) { LOG("Failed to set " + signalToString(unixSignals[i].signal) + "'s signal handler!"); } else { LOG(signalToString(unixSignals[i].signal) + "'s signal handler set sucessfully!"); } } } In my signal handler (HandleSignals method), immediately after trapping a signal, I log something and set the default handler... This breaks out of the loop that occurs when returning from the signal handler. // ExceptionHandlingCpp.cpp void ExceptionHandlingCpp::HandleSignals(int pSignal, siginfo_t *pInfo, void *pContext) { LOG("ExceptionHandlingCpp::HandleSignals(int, signinfo_t*, void*)"); LOG("signal = " + signalToString(pSignal)); UnregisterSignals(pSignal); LOG("Returning from exception handler..."); } void ExceptionHandlingCpp::UnregisterSignals(int pSignal) { LOG("UnregisterSignals(int)"); struct sigaction defaultAction {}; defaultAction.sa_handler = SIG_DFL; if(sigaction(pSignal, &defaultAction, nullptr) == -1) { LOG("Error in resetting action for " + signalToString(pSignal)); } else { LOG("Successfully reset " + signalToString(pSignal) + "'s action to default!"); } } When I test this code by raising SIGSEGV (as shown below), void ExceptionHandlingCpp::DereferenceNullPtr () { LOG("DereferenceNullPtr()"); int* ptr = nullptr; LOG("Raising exception..."); int value = *ptr; } everything works as expected. Signal handler is invoked, default handler is set and the process immediately quits. But when I try to raise a SIGFPE, void* ExceptionHandlingCpp::DivisionByZero ([[maybe_unused]] void* pParms) { LOG("DivisionByZero()"); int num1; int num2; int result; num1 = 5; num2 = 0; LOG("Raising exception..."); result = num1 / num2; LOG("Returning from DivisionByZero() method"); return nullptr; } my signal handler is not invoked (as shown in the logs below). The process doesn't terminate either. It seems that the flow simply 'walks over' this division by zero instruction as if nothing happened and returns from that method, which shouldn't have happened, as the process should've terminated after reaching my signal handler. RegisterSignals() SIGFPE's signal handler set sucessfully! SIGSEGV's signal handler set sucessfully! SIGKILL's signal handler set sucessfully! .... DivisionByZero() Raising exception... Returning from DivisionByZero() method .... AppDelegate.applicationWillBecomeActive(_:) AppDelegate.applicationDidBecomeActive(_:) ... // UI is displayed Why is SIGFPE not raised? What am I missing here?
4
0
517
Dec ’24
IOS warning: instance method 'userNotificationCenter(_:willPresent:withCompletionHandler:)' nearly matches optional requirement
In an iOS project, I have implemented the following delegate method to handle Notifications when app is in foregorund: func userNotificationCenter (_ pNotificationCenter : UNUserNotificationCenter, willPresent pNotification : UNNotification, withCompletionHandler pCompletionHandler : @escaping (UNNotificationPresentationOptions) -> Void) -> Void { // When app is in foreground, notification is directly sent to // the app by invoking this delegate method. } When building, I'm getting the warning. But my implementation is correct according to documentation. Why is this method not recognized as the implementation of the UNUserNotificationCenterDelegate protocol? This is the warning in text format: Showing All Issues <file path & line number>: Instance method 'userNotificationCenter(_:willPresent:withCompletionHandler:)' nearly matches optional requirement 'userNotificationCenter(_:willPresent:withCompletionHandler:)' of protocol 'UNUserNotificationCenterDelegate' This is the image of the warning: Am I missing some flag in a compiler setting? My env: Xcode 15.2, Swift 5.0, Deployment target - iOS 14.0+.
3
1
970
Feb ’24
Notification Service Extension restrictions
I have an iOS app which uses Notification Service Extension (NSE) to process incoming notifications before it displayed to user. In NSE, as part of initialization of my app, I need to iterate through a 2D array. There are roughly 65k iterations. I've noticed that this iteration fails somewhere in between and the NSE process crashes... I can say it crashes because the logs stopped in between the iterations. This results in 'unmodified notification' getting displayed immediately, whereas NSE is granted 30 sec of background execution. My question is, why does this happen? The above iteration of 2D array works in app, but fails in NSE. Is there some kind of restriction on background extensions? - the documentation only talks about a time limit of 30sec. If there is some kind of restriction (like CPU and memory), how does one know this and code for it... since Apple did not provide any documentation. Or perhaps, there is a completely different reason?
3
1
1.2k
Jun ’24
Periodic iOS background execution
An app has some periodic light background work, which is expected to run even when app is suspended. Ofc, if user has swiped up, nothing can be done. When swiped from app switcher, if app was in Suspended state, its not informed else, in every other state (including background), applicationWillTerminate(_:) is invoked When application is not killed by user and is in suspended state, I want to run some small tasks at certain intervals. According to this documentation, Background fetch is the way to go. This post by Apple DTS also suggests the same i.e., Background Fetch (BGAppRefreshTask) for periodic background execution. Example: The sample background task is to count 10s in multiple of 10s. Launch handler is registered with the background task ID configured in info.plist file in application(_:didFinishLauchingWithOptions:). I have the following code invoked from in applicationDidEnterBackground(_:), func initiateBackgroundTask(id: String) {         let currentDate: Date = Date.now         guard let futureDate: Date = Calendar.current.date(byAdding: .minute, value: 1, to: currentDate) else {             NSLog(AppDelegate.TAG + "Error in obtaining future date!")             return         }         let taskRequest: BGAppRefreshTaskRequest = BGAppRefreshTaskRequest(identifier: id)         // Schedule background task         taskRequest.earliestBeginDate = futureDate         do {             // Note: Background processing doesn't work in Simulator.             // Submitting in Simulator leads to BGTaskScheduler.Error.Code.unavailable error.             try BGTaskScheduler.shared.submit(taskRequest)             NSLog(AppDelegate.TAG + "Task submitted successfully!")         } catch {             NSLog(AppDelegate.TAG + "error = " + String(describing: error))             return         }         NSLog(AppDelegate.TAG + "Background task scheduled successfully!!")         NSLog(AppDelegate.TAG + "Scheduled date = %@ | Present date = %@", String(describing: taskRequest.earliestBeginDate), String(describing: Date.now))     } The following method is invoked from launch handler: func taskRefreshCount1Min(task: BGAppRefreshTask) {              // Set expiration handler         task.expirationHandler = {            NSLog(AppDelegate.TAG + "Background execution time about to expire!")            NSLog(AppDelegate.TAG + "Remaining time = %fsec | %@", AppDelegate.app.backgroundTimeRemaining, String(describing: Date.now))             NSLog(AppDelegate.TAG + "Unable to complete task!!")             task.setTaskCompleted(success: false)         }         for i in 1...6 {             let presentDate: Date = Date.now             NSLog(AppDelegate.TAG + "%d) %@", i, String(describing: presentDate))             Thread.sleep(forTimeInterval: 10)         }         NSLog(AppDelegate.TAG + "Task complete!")         task.setTaskCompleted(success: true)     } My expectation is that iOS will execute the launch handler 1 min after scheduling the background task. But that never happens, even after hours. Task did get executed within 20 mins (Sample output shown below), but every other time, I waited for more than an hour and task was never executed. // Output ... Task submitted successfully! Background task scheduled successfully!! Scheduled date = Optional(2023-02-06 09:19:46 +0000) | Present date = 2023-02-06 09:18:46 +0000 TaskRefreshCount1Min(task:) 1) 2023-02-06 09:35:12 +0000 2) 2023-02-06 09:35:22 +0000 3) 2023-02-06 09:35:32 +0000 The documentation does state, However, the system doesn’t guarantee launching the task at the specified date, but only that it won’t begin sooner. But I didn't expect the results to differ by such a huge margin. Apparently others have also faced this 'inconsistent scheduling' in iOS. Background fetch with BGTaskScheduler works perfectly with debug simulations but never works in practice How make iOS app running on background Every one minute in Swift? In links I have pasted above, despite Background Fetch being the most suitable for periodic light tasks, Silent Push Notifications were suggested. I'm currently checking out Silent Push Notifications, but iOS recommends a frequency of not more than 3 per hour. Summarising my questions from this exercise: Is there something wrong with the code? What technique should I use? Background fetch, silent notifications or anything else? In Background Fetch, how do you schedule background tasks periodically? There's an instance property called earliestBeginDate to denote the minimum time after which the task can happen.
2
3
3.2k
Feb ’23
Application lifecycle - AppDelegate method Vs Notification Center
There are two ways to capture application transitioning to foregorund/background and inactive/active. Lets take the event received when app transitions to the inactive state as an example. Implement the corresponding AppDelegate method - applicationWillResignActive(_:) func applicationWillResignActive(_ application: UIApplication) { Log("applciationWillResignActive(_:)") } Register for willResignActiveNotification during startup NotificationCenter.default.addObserver(forName:UIApplication.willResignActiveNotification, object: nil, queue: .main) { (broadcastNotification: Notification) in Log("Received " + String(describing: broadcastNotification.name)) } When I tested in code, both works. First, the applicationWillResignActive(_:) delegate method is invoked and then, willResignActiveNotification is received (as mentioned in the documentation). But I'm unable to decide which to use... even after reading their documentation. What is the recommendation? If both are fine, then there are two different ways to achieve the same thing... there's gotta be some purpose, right?
2
0
1.5k
Aug ’23
Exclude notification delegate methods for tvOS using @available
UserNotifications framework helps the app to handle all notification related aspects. The userNotificationCenter(_:didReceive:withCompletionHandler:) delegate method is used to handle user's action (dismiss, selected button, tap, text input etc.). As evident from the documentation, this method is available in iOS and iPadOS but not tvOS. The @available attribute is one way to exclude methods. @available(iOS 10.0, *) func userNotificationCenter (_ pNotificationCenter: UNUserNotificationCenter, didReceive pResponse: UNNotificationResponse, withCompletionHandler pCompletionHandler: @escaping () -> Void) -> Void { // Handle user's action on a notification. ... } When the delegate method is declared as above, I expect it to be included only for iPadOS and iOS. When building for tvOS, I get the following error: error: 'UNNotificationResponse' is unavailable in tvOS error: cannot override 'userNotificationCenter' which has been marked unavailable Clearly, it is included for tvOS also. Another approach is using #if os(iOS) and it does work... I don't get the above errors, meaning the code is not included for tvOS. But I want to understand how to achieve this using @available attribute because I have other situations where I need to exclude methods for certain versions of the same OS. How do I exclude userNotificationCenter(_:didReceive:withCompletionHandler:) for tvOS using @available attribute?
1
0
1k
May ’23
Universal link not passed to SceneDelegate when app runs in the background
I have a SwiftUI app which supports a launch from a configured universal link. I'm following this documentation. When the app is not running and I tap a universal link, app launches. This proves the association of the website and the app. Now, when the app is already running in the background, and then I tap a universal link, things don't work as expected. Quoting from the documentation If your app has opted into Scenes, and your app is not running, the system delivers the universal link to the scene(:willConnectTo:options:) delegate method after launch, and to scene(:continue:) when the universal link is tapped while your app is running or suspended in memory. I've implemented scene(_:continue:) in my SceneDelegate but it never gets invoked. I'm pasting some relevant logs AppDelegate.application(_:willFinishLaunchingWithOptions:) AppDelegate.application(_:didFinishLaunchingWithOptions:) AppDelegate.application(_:configurationForConnecting:options:) SceneDelegate.scene(_:willConnectTo:options:) SceneDelegate.sceneWillEnterForeground(_:) NSNotificationName(_rawValue: UIApplicationWillEnterForegroundNotification) SceneDelegate.sceneDidBecomeActive(_:) NSNotificationName(_rawValue: UIApplicationDidBecomeActiveNotification) Now, I background the app... SceneDelegate.sceneWillResignActive(_:) NSNotificationName(_rawValue: UIApplicationWillResignActiveNotification) SceneDelegate.sceneDidEnterBackground(_:) NSNotificationName(_rawValue: UIApplicationDidEnterBackgroundNotification) App runs in the background... Now, I tap a Universal link SceneDelegate.sceneWillEnterForeground(_:) NSNotificationName(_rawValue: UIApplicationWillEnterForegroundNotification) SceneDelegate.sceneDidBecomeActive(_:) NSNotificationName(_rawValue: UIApplicationDidBecomeActiveNotification) As shown in the logs above, there is no trace of scene(_:continue:). func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { Log("SceneDelegate.scene(_:continue:)") guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let universalLink = userActivity.webpageURL else { Log("Not launched via universal links!") return } Log(String(format: "userActivities = %@", String(describing: userActivity))) Log(String(format: "universalLink = %@", universalLink.absoluteString)) StaticContext.dataFromMainApp = universalLink.absoluteString StaticContext.viewController.updateLabelWithLink() } What am I missing here?
1
1
2.0k
Feb ’24
64-bit Apple Watches with 64-bit pointer sizes?
I have a watchOS app written in C++, Swift and SwiftUI. This app is meant only for 64-bit architecture. I know Apple uses the arm architecture, so I thought arm64 will be the arch of latest AppleWatches. I tested my code in x64 simulator (Intel Mac) and arm64 simulator (silicon Mac) - no trouble. Next is to test on a device - AppleWatch Series 9, model: A2984. I set the arch to arm64 and my application failed to build, with Xcode showing a popup about arch mismatch... which is due to the app's arch begin different from device's arch. When I change Xcode arch setting to arm64_32, then I don't get this popup, but my builds fail. When I read about arm64_32, I realised that this is a version of arm64 with 32 bit pointers... not the arm64 with 64 bit pointers. Looking at the specification of AppleWatch Series 9, Apple has confirmed that it has 64 bit CPU, but the instruction set is unknown. This wiki page about different AppleWatch CPUs is marked TBC for AppleWatch Series 9. From Xcode, I got to know that this unconfirmed arch is arm64_32. This completely breaks my code. Are there any 64-bit watches with 64-bit pointer sizes? What is Apple's future in this area? Do they plan to release AppleWatches that support 64-bit pointers or it's always going to be 32?
1
0
1.3k
Jul ’24
SwiftUI Vs AppKit for a complex and dynamic UI
I'm looking to develop a very rich networking macOS app (like social media apps) operated by very large number of users, each user is able to create a number of windows, operate/view each of them, able to customize the app to his liking etc. The UI is expected to be very rich and dynamic. The question is, should I choose AppKit or SwiftUI? I have a basic understanding of SwiftUI, its declarative way of defining UI layouts and populating it with data. Not sure if SwiftUI can handle a very rich and dynamic UI customised by large number of users. Any thoughts? What works best in this scenario? What is Apple's recommendation?
1
1
442
Feb ’25
Using handleExternalEvents scene modifier to route external events to the correct scene
In an iPadOS SwiftUI app supporting multiple scenes, each Scene responds to a particular way in which the app was launched. If app was launched by tapping an associated file or a deep link (custom URL), then, the URLHandlerScene is invoked. If app was launched by QuickAction (long tap on the app icon), then another Scene is invoked etc. Each Scene has a purpose and responds to a particular launch. But after defining handlesExternlEvents(matching:) scene modifier, the scene was not getting launched when user taps the associated file or the app's Deeplinks was invoked. @main struct IOSSwiftUIScenesApp: App { var body: some Scene { DefaultScene() URLHandlerScene() .handlesExternalEvents(matching: ["file://"]) // Launched by an associated file .handlesExternalEvents(matching: ["Companion://"]) // Launched by Deeplink. // Other scenes } } struct URLHandlerScene: Scene { @State private var inputURL: URL // Store the incoming URL init() { self.inputURL = URL(string: "Temp://")! } var body: some Scene { WindowGroup { URLhandlerView(inputURL: $inputURL) .onOpenURL(perform: { (fileURL: URL) in log(String(format: "URLhandlerView().onOpenURL | Thread.current = %@", String(describing: Thread.current))) log("fileURL = " + String(describing: fileURL)) inputURL = fileURL }) } } } As shown above, I've attached handlesExternalEvents(matching:) modifier with "file://" for the associate file and "Companion" is my custom URL scheme. As per the scene matching rules documented here, my URLHandlerScene should get launched, but every time I launch the app using associated file or 'open' a Deeplink, the DefaultScene is always launched. What is missing here? Can someone please help?
1
0
128
Apr ’25
Set swift macros from cmake
I have a swift project which uses macros for conditional compilation. How to set macro: Go to Target -> BuildSettings -> Swift Compiler - Custom Flags -> Active Compilation Conditions and add your macro (Ex: sample macro - ELDIABLO in this case). Then the following swift code, #if ELDIABLO NSLog("ELDIABLO macro detected!") #else NSLog("ELDIABLO macro not detected!") #endif ...produces the desired output. ELDIABLO macro detected! Now, how do I achieve this through cmake? When I do the following, set(CMAKE_Swift_FLAGS "${CMAKE_Swift_FLAGS} ELDIABLO") the macro gets added to Swift Compiler - Custom Flags -> Other Swift Flags (shown in the above image besides Active Compilation Conditions). But it must be added in Swift Compiler - Custom Flags -> Active Compilation Conditions, else (here's why), it will not get detected in swift files. How can I set swift macros in Active Compilation Conditions through cmake?
0
0
776
Sep ’22
Request Notification Service Entitlement to filter remote notifications IOS
I have a sample app to test push notifications. The app has a Notification Service Extension to intercept remote notifications, update/enhance the content and then display it to user. But there are some scenarios where the app shouldn't end up displaying a notification to user... Like, due to slow internet, the time taken to download media content (like images) exceeds the 30 sec time limit. There are more app specific scenarios. Basically, every push received from APNS need not be shown to user. Notification Service Extension did not have this power to suppress remote notifications. In the end, it will always display a notification to user. From iOS 13.3 onwards, Notification Service Extension has the ability to filter out these remote notifications. The first step is (quoting form the documentation), This entitlement allows a notification service extension to receive remote notifications without displaying the notification to the user. To apply for this entitlement, see Request Notification Service Entitlement. When I click on the hyperlink (you need to be an account holder to view that page), Apple asks for some info like 'App Store URL', 'When extension runs, what system and network resources does it need?' and so on. I only have a sample app to help understand the aspects of notifications. I don't have any full-fledged app in AppStore... yet... and I have no clue what all my full-fledged app will be doing in the extension (since I'm just in learning stage). I only have a sample app, where I log and perform some basic string operations when my extension is invoked. Due to this, I'm unable to see the extension filtering out notifications. How does anyone test this feature? Has anyone tried it? Really appreciate any guidance. I'm not sure how to proceed..
0
0
1.6k
May ’23
'Import-path' in Import declaration's documentation
According to swift documentation, the following describes the various forms of the Import declaration. Grammar of an import declaration import-declaration → attributes? import import-kind? import-path import-kind → typealias | struct | class | enum | protocol | let | var | func import-path → identifier | identifier . import-path Questions: Attributes and import-kind are optional. import-kind refers to the type of the import (class, struct, enum etc etc).... defined in the second line. Does attributes refers to @available which can used to add an import for a specific platform version? In the 3rd line, I understand that identifier here refers to the names of modules, classes, funcs etc., but what does import-path mean? Can I give the actual path in disk? What all comes under this? I couldn't understand from this documentation.
0
0
631
Aug ’23