Post

Replies

Boosts

Views

Activity

Show main window of SwiftUI app on macOS Sequoia after auto start
It seems like it is no longer possible to open the main window of an app after the app has been launched by the system if the "Auto Start" functionality has been enabled. I am using SMAppService.mainApp to enable to auto start of my app. It is shown in the macOS system settings and the app is automatically started - but the main window is not visible. How can I change this behaviour so the main window of the app is always visible when started automatically? I have not noticed this behaviour before the release of macOS Sequoia. My app is using Swift 6 and the latest version of macOS and Xcode. Regards
6
2
344
Sep ’24
Porting Thread & Delegate Code to Swift 6 Using Tasks
Hello, I have a lot of apps and I am currently trying to port them over to Swift 6. I thought that this process should be relatively simple but I have to admit that I have a lot of trouble to understand how the Concurrency system works. Let's start with some code that shows how I am currently working when it comes to asynchronous work in my apps: I have a Model that is marked with @Observable. Inside this model, a Controller is hosted. The Controller has its own ControllerDelegate. The Model has a search function. Inside this function a lot of IO stuff is executed. This can take a lot of time. Because of this fact, I am doing this in a separate Thread. I all is put together, it looks a little bit like this: @main struct OldExampleApp : App { @State private var model = Model() var body: some Scene { WindowGroup { ContentView() .environment(self.model) } } } struct ContentView: View { @Environment(Model.self) private var model var body: some View { if self.model.isSearching { ProgressView() } else { Button("Start") { self.model.search() } } } } protocol ControllerDelegate : AnyObject { func controllerDidStart() func controllerDidEnd() } class Controller { weak var delegate: ControllerDelegate? func search() { let thread = Thread { DispatchQueue.main.async { self.delegate?.controllerDidStart() } // Do some very complex stuff here. Let's use sleep to simulate this. Thread.sleep(forTimeInterval: 2.0) DispatchQueue.main.async { self.delegate?.controllerDidEnd() } } thread.start() } } @Observable class Model { private(set) var isSearching = false var controller = Controller() init() { self.controller.delegate = self } func search() { self.controller.search() } } extension Model : ControllerDelegate { func controllerDidStart() { self.isSearching = true } func controllerDidEnd() { self.isSearching = false } } This works perfectly fine and by that I mean: The task is run in the background. The main thread is not blocked. The main window can be dragged around, no beach ball cursor etc. Now comes the Swift 6 part: I want to merge the Model and Controller into one class (Model). I still want the Model to be Observable. I want to run arbitrary code in the Model. This means that the code is not necessarily a prime candidate for await like getting data from a web server etc. The main thread should not be blocked, so the main window should still be movable while the app calculates data in the background. I have this example: struct ContentView: View { @Environment(Model.self) private var model var body: some View { if self.model.controller.isSearching { ProgressView() } else { Button("Search") { Task { await self.model.controller.heavyWork() } } } } } @Observable final class Model : Sendable { @MainActor var controller = AsyncController() init() { } } @Observable @MainActor class AsyncController { private(set) var isSearching = false public func heavyWork() async { self.isSearching = true Swift.print(Date.now) let i = self.slowFibonacci(34) Swift.print(i) Swift.print(Date.now) self.isSearching = false } func slowFibonacci(_ n: Int) -> Int { if n <= 1 { return n } let x = slowFibonacci(n - 1) let y = slowFibonacci(n - 2) return x + y } } I come from a C# background and my expectation is that when I use a Task with await, the main thread is not blocked and the Code that is called inside the Task runs in the background. It seems like the function is run in the background, but the UI is not updated. Because I set the isSearching flag to true, I would expect that the app would display the ProgressView - but it does not. I changed the code to this: public func heavyWork() async { self.isSearching = true Swift.print(Date.now) let i = await self.slowFibonacci(20) Swift.print(i) Swift.print(Date.now) self.isSearching = false } func slowFibonacci(_ n: Int) async -> Int { let task = Task { () -> Int in if n <= 1 { return n } let x = await slowFibonacci(n - 1) let y = await slowFibonacci(n - 2) return x + y } return await task.value } This seems to work - but is this correct? I have this pattern implemented in one of my apps and there the main thread is blocked when the code is run. So I think it all comes down to this: Is it possible, to run a arbitrary code block (without an await in it) in a Task, that can be awaited so the main thread is not blocked? The class (or actor?) that contains the function that is called via await should be Observable. Or should I simply keep my Swift 5 code and move on? :D Regards, Sascha
3
0
665
Jul ’24
Did macOS Sonoma add additional file read-write restrictions?
Hello, I have an app in the App Store that is your typical cleaner app. The app searches for files from deleted apps and can remove them. Under macOS Sonoma the app does no longer work. When deleting files, the following message appears in the Console: System Policy: App(1636) deny(1) file-write-unlink The app has scoped bookmarks for the folder and additionally I added Full Disk Access for my app - it still does not work? Is there another layer on top of that? The app works perfectly fine on Big Sur, Monterey and Ventura. Regards, Sascha
2
0
975
Sep ’23
Force Refresh Volume to Get Current Amount of Free Bytes From File System Without Any Caches
Hello, in my app I need the current amount of free space on a certain volume. I use the following code to the this information: if let mountedVolumeURLs = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil) { for volumeUrl in mountedVolumeURLs { guard let values = try? self.url.resourceValues(forKeys: [.volumeTotalCapacityKey, .volumeAvailableCapacityKey]) else { continue } let totalBytes = values.allValues[.volumeTotalCapacityKey] as? UInt64 ?? 0 let freeBytes = values.allValues[.volumeAvailableCapacityKey] as? UInt64 ?? 0 // This is not always correct. } } When writing to a volume and then calling the code above, a wrong amount is returned. I suspect that there is still some cacheing going on. Is there any function that I can call to get this information from the file system without any cacheing? I guess an alternative would be to get a list of all files, get their sizes, add them up and subtract that number from the total bytes of the volume. But maybe there is a more elegant solution. I also tried something like this: let handle = try FileDescriptor.open(volumeUrl.path, .writeOnly, options: .create, permissions: .ownerReadWrite, retryOnInterrupt: true) fcntl(handle.rawValue, F_FULLFSYNC) But this did not help. I would appreciate any help. Regards
1
0
600
Aug ’23
Loading NSImage from icns file takes very long for Visual Studio Code icon
I am trying to load an app icon from an icns file. I am using the following code: guard let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.microsoft.VSCode") else { return nil } let icon = NSWorkspace.shared.icon(forFile: url.path) This seems to work very fine for all apps that I tested. All of them? No. Loading the icon for Visual Studio Code takes 4-5 seconds. Are there any other, faster methods? I also tried to use NSImage(contentsOfFile: url.path) which also takes very long. I have uploaded the icns file here: https://sascha-simon.com/Code.icns How can this behavior be explained?
4
0
1k
Aug ’23
Get Model Identifier of Device in Network
I have a couple of Apple devices in my network (both computers and mobile devices). I was wondering if it is possible, to get the model identifier like Macmini9,1 if I have the IP address and/or the Mac address. I am using the NetServiceBrowser and NetService classes to find the devices. If the device has the AirPlay service, I can get the model identifier. However, not all of my devices seem to implement/publish this service. Is there a more general approach? macOS seems to know this information. In the Finder a specific icon is shown, so I guess the model identifier must be published somehow.
4
0
1.4k
Mar ’23
Xcode creates schemes for some referenced Swift Packages
Xcode always creates targets /schemes for referenced Swift packages. In my app there are 14 Swift packages that are referenced. My app itself has two targets (app and an auto launcher) and Xcode creates targets for two schemes. I don't understand why those schemes are created (and why not for every package). Is there any way to disable this feature? Can someone explain why Xcode creates schemes for referenced Swift packages? I deleted the schemes several times, but Xcode simply recreates them.
3
1
1.9k
Sep ’22
Porting existing LoginItem Logic to the new SMAppService
Hello, I have many apps that use LoginItem logic to enable the auto start. I always used the function SMLoginItemSetEnabled to enable or disable the launch of the LoginItem. I am using macOS Ventura Beta for quite some time now and I noticed that whenever I launch one of my apps, a message appears. It says that my app added some functions. When I click on the message, the Login Items section of the System Preferences opens and my recently opened app is listed under "Allow in the Background". I want to port my apps to the new logic but I am not quite sure how to do that. I don't want my apps to appear under the "Allow in the Background" section but rather in the "Open at Login" section. I am using the following code to enable or disable the auto start under macOS 13.0+: if autoLogin { try? SMAppService.mainApp.unregister() } else { try? SMAppService.mainApp.register() } This works very well. If I check my switch, the app appears under the "Open at Login" section. If I disable the switch, the app disappears from this list. Great, this is what I want. However...when I restart my Mac, the app is launched automatically. And now there is a new entry in the "Allow in the Background" section again. What am I doing wrong? Should I register or unregister the LoginItem instead? This is not really what I want, it appears in the wrong section and I think this is confusing for users. I read somewhere else that the user consent is stored, even when resetting with sfltool or something like that. Does the item appear in this section (and is launched automatically) because I launched the app earlier without my new logic? Is this simply a bug? has anyone a working example of how to properly support auto launching an application under macOS Ventura and earlier versions? Regards, Sascha
6
0
3.0k
Sep ’22
Can not get FileHandle on MS-DOS file systems: The operation couldn’t be completed. Operation not permitted
Hello, I am trying to get a FileHandle for a specific file on an external hard disk. My app is sandboxed and in order to be able to get a FileHandle I am showing a NSOpenPanel so the user can grant permission to access this disk. After the user has granted permissions, I use the following code to get the handle: guard let readDescriptor = try? FileDescriptor.open(readUrl.path, FileDescriptor.AccessMode.readWrite) else { return } I get the following error when this code runs: The operation couldn’t be completed. Operation not permitted What I don't understand is the fact that this code only fails on disks that have the MS-DOS file system. I have tested various USB sticks, SD cards and external hard drives. It works fine for the APFS, but once I format the device as MS-DOS, the code fails. Has anyone any idea why that is? Regards, Sascha
3
0
1.3k
May ’22
App Store Connect API is very slow
I am using the App Store Connect API to receive information about my app. I have noticed that the API is so extremely slow. Are there any tipps to speed up the requests? Take this request for example: https://api.appstoreconnect.apple.com/v1/builds?filter[app]=1234854699&limit=1&fields[builds]=iconAssetToken To me, this seems like a pretty straightforward request. Out of all build, get the latest one and return it. However, this request takes between 3 and 5 seconds. I need to send this request for each of my apps, so getting the builds alone can take up to 20 seconds which is not acceptable. Other requests are very slow as well. For simple requests like that I expect response times within a couple of hundreds milliseconds. Am I missing something? I have used Postman to send the request. I did not send any additional headers, only the Authorisation one.
3
1
3.2k
Oct ’21
Disable cacheing before reading from a file using Swift
Hello, I am trying to implement some sort of minimal I/O benchmarking feature. I can calculate the write speeds with the following code: guard var handle = FileHandle(forUpdatingAtPath: url.path) else { return } var writtenData: UInt64 = 0 var totalWrittenData: UInt64 = 0 var writeSpeed: UInt64 = 0 let data = Data(repeating: 0, count: 16 * 1024 * 1024) // 16 MB test block let startTime = Date.now.timeIntervalSince1970 while Date.now.timeIntervalSince1970 - startTime < 5.0 { handle.write(data) try? handle.synchronize()self.totalWrittenData += blockSize let duration = Date.timestamp - self.startTime writeSpeed = UInt64(Double(self.totalWrittenData) / duration) if writtenData > blockSize * 16 { writtenDara = 0 try? handle.seek(toOffset: 0) } } // On my PCE-Express SSD this results in roughly 1800MB/s. This value matches the value reported by other benchmarking apps. // Remove everything at the end of the file try? handle.truncate(atOffset: blockSize) try? handle.synchronize() try? handle.seek(toOffset: 0) var index = 0 while index < 5 { autoreleasepool { var startTime = Date.timestamp let bytes = try? handle.readToEnd() let duration = Date.timestamp - startTime          Swift.print(bytes?.count, duration) try? handle.seek(toOffset: 0) } index += 1 } The code works - in theory. Although I really like that the operating system and drivers do some cacheing to optimise performance - in my use case I don't want files to be cached. The read results are always around 8GB/s for my PCE-Express SSD and also for some older USB Sticks, so the data seems to come directly from the memory. I found a couple of other threads which led me to some older C functions, specifically this code: // Open file. let fd = fopen(fileUrl.path, "r") // Find the end fseek(fd, 0, SEEK_END) // Count bytes let fileByteSize = ftell(fd) // Return to start fseek(fd2, 0, SEEK_SET) // Disable cache setvbuf(fd2, nil, _IONBF, 0) // Find the end fseek(fd2, 0, SEEK_END) let readBytes = fread(pointer, 1, fileByteSize, fd2) readBytes += UInt64(readBytes.count) When executing this code (or parts of it), it also works fine - but the data is also coming from the cache. What am I missing? I also tried the FileManager. Of course I am not an expert, however I think that the FileManager is "just" a wrapper around all those C functions. Is there any way to ignore the cacheing mechanism and tell macOS / the driver to read the data directly from the drive? Regards, Sascha
3
0
1.3k
Feb ’22
CoreBluetooth stopped working in my app
Hello, I am the developer of an app that is published on the App Store. This app connects to Bluetooth devices to read their battery levels. However, it seems that CoreBluetooth, which is used to discover and connect to devices, has completely stopped working on macOS Monterey. The delegate methods are no longer triggered. When I start my app I see the following messages in the output area: [CoreBluetooth] No name or address The Console app shows a lot of this messages: error 14:56:23.730415+0200 bluetoothd Server.Core sdp attributes is empty I don't know if this is related in any case. Are there any new entitlements or something like that? I did not change any single line and on Big Sur, everything works fine. A customer of mine, which also uses the beta version, already contacted me, so this seems to be a general issue - at least with my app? The app also works fine on Big Sur for the customer.
4
0
4.7k
Oct ’21
I accidentally created an iOS version of my app in App Store COnnect - How do I delete it?
Hello, I have a macOS app that is available on the Mac App Store. Yesterday I wanted to copy the link of my app. I opened App Store Connect which was still loading. I saw the correct link and clicked it. However, just in the moment when I clicked it, the page layout changed and the link became the link "Create iOS app". HOw I have my macOS build that is released and a new iOS version 1.0 for my app. I can not delete the iOS version. Is it possible or do I have to live with a useless build that will anooy me for the next couple of years? Regards
1
0
901
Sep ’21
Creating new text file using Finder Sync API results in deny(1) file-write-create Sandbox error
I am trying to develop an application that also contains an extension for the Finder. The extension can be used to create a new text file at the current Finder location. I have only added the following permissions to the entitlements file for both the main app and the extension: com.apple.security.files.user-selected.read-only I have implemented the FIFinderSync protocol in my extension. I added a menu item to create a new text file. Whenever I execute this method, the following error is shown in the Console: Sandbox: Extension(4032) deny(1) file-write-create /Users/username/Documents/New Text.txt The simplest way to get around this error is to add the following key: com.apple.security.temporary-exception.files.home-relative-path.read-write However, applications using this entitlement will be rejected during app review. The extension is initialised with this code: FIFinderSyncController.default().directoryURLs = [URL(fileURLWithPath: "/")] How can I prevent this error to be thrown? I could use security scoped bookmarks, open a dialog and then save this bookmark. However, I have tested plenty of extension available in the App Store and none of them have shown any NSOpenPanel, the files have been created without any problems. Any help is appreciated.
5
0
1.9k
Aug ’21
I ran out of Developer ID Certificates - Second Developer Account?
Hello, I ran into a big problem for which I don't know how to proceed. Every developer can create up to five Developer ID certificates. Those certificates are used if you want to release your app outside the Mac App Store. I have an app on the Mac App Store that also offers a helper application, which is downloadable on my homepage. I have created a new version of this helper application and wanted to send it to the notarization service of Apple. Xcode displayed an error that I do not have a valid Devloper ID certificate (because the private key is missing) and I can not create a new one because I already created five certificates. I can download the certificates without problem, but I can not get the private key. At least one developer certificate was created on my machine, but It does not seem to work. Because I assumed that there is no way of getting these certificates back (always create backups!), I contacted Apple to see if they are willing to revoke any old certificates so I can create a new one (I know that at least the first two certificates are not being used, so they could be revoked, but Apple does not revoke old certificates). Right now, Apple decides whether they grant me an exception to create additional certificates. Let's assume Apple declines this request...what can I do? Can I create a second developer account? I don't care about the 99€, but it does not seem that this is allowed. Big Sur will be released tomorrow and I really need to sign this app. And yes, I know that I should keep a backup copy of my private keys in the future ;) Is there anything I can do (especially if Apple declines the request)? Any help is highly appreciated. Kind Regards, Sascha
5
0
3.0k
Nov ’20