Post

Replies

Boosts

Views

Activity

UIDocumentViewController missing page background in browser on iPadOS 27
Since iPadOS 18, UIDocumentViewController has contained a document browser that shows a white page with rounded corners against a background of your choice, with the app name and "Create Document" buttons on the page. For instance, when you launch Pages, you see a white rounded page rectangle against a background of swirly orange, with “Choose a Template” and “Start Writing” buttons on the white page. In Numbers, there’s a green swirly background. In apps built and run on iPadOS 27, however, the white page with rounded corners is entirely missing, making the browser screen very ugly, with the “New Document” button translucent directly against whatever background is set. This can be reproduced simply by creating a new iOS "Document App" in Xcode 27 and building on iPadOS 27. I assume this is a bug, since if you turn on exception breakpoints, you see the following exception breakpoint triggered during launch: Exception = (NSException *) "[<_UIDocumentLaunchViewController 0x10732b200> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _pageContainerView." I have thus reported it as FB23418746. I am curious, though, whether it is a design decision to remove the page background on iPadOS 27, or whether I am missing some sort of setting in the UIDocumentViewController’s launch options for restoring the page. (I hope it’s not intentional, as I like the page, and without it, the black app name gets lost against darker or busier backgrounds.) (I did try to include screenshots showing the issue when I first went to post this message, but doing so resulted in my IP address being blocked access to the forums for a week because of the forums’ new security measures.)
Topic: UI Frameworks SubTopic: UIKit Tags:
2
0
209
2w
NSTableView: checking for mouse-driven selection changes on macOS 27
I have an NSTableView used as a source list and, alongside it, two editors. When the user selects anything in the table view, its content is opened in the editor that has the focus. When the user Opt-clicks an item in the table, though, the content is opened in the other editor, making it easy for the user to load something in the other editor without having to change the focus first. This has worked for many years using NSTableView.selectiondDidChange / the NSTableViewDelegate as follows: func tableViewSelectionDidChange(_ notification: Notification) { if let event = tableView.window?.currentEvent, event.type == .leftMouseUp || event.type == .leftMouseDown, // (Real app does some other checks here too.) event.modifierFlags.contains(.option) { openInOtherEditor() return } openInCurrentEditor() } However, on macOS 27, it seems that things need to be done differently because of the transition to gesture recognisers for event handling. According to the WWDC video "Modernise Your AppKit App", and to Tech Note TN3212, currentEvent can no longer be relied upon to provide the event that actually triggered an action in NSControl subclasses: The transition to gesture recognizers on NSControl objects changes the timing of when AppKit delivers control action messages with respect to event processing. As a result, currentEvent no longer returns the event that triggered an action. It's unclear whether this new limitation refers only to NSControl.action or to all mouse-driven actions, but from the context and what the rest of the Tech Note has to say, I assume it's the latter. (Especially since you are no longer supposed to override mouseDown(with:), and the Console warns about gestures being disabled if you do override mouseDown(with:) in an NSTableView subclass on macOS 27.) currentEvent still seems to work fine in this situation in the first macOS 27 beta, but it sounds as though we cannot rely on this continuing to be the case. If we should no longer be using currentEvent, then, what should we use instead to determine whether a selection change was triggered by a mouse click? The Tech Note and WWDC video have nothing to say about this. They simply say that instead of overriding mouseDown(with:), you should use the selection-did-change delegate methods, which is of no help here. (By contrast, checking the modifier flags is still straightforward; the Tech Note says to use NSEvent.modifierFlags instead of currentEvent.modifierFlags.) Two solutions sprung to mind, but neither worked: Check tableView.clickedRow != -1 in the selectionDidChange delegate method/notification response. This doesn't work, however, because clickedRow has been reset to -1 by the time NSTableView.selectionDidChange is sent. Add an action to the table view and check clickedRow there. This doesn't work either, though, because although clickedRow is available in the action method, I would now have to load content in response to both an action and a selection change, and since the selection changes before the action is called, there is no way of telling my selection-did-change method not to load in the main editor if Option is held down in the action. The only solution I have found is to override selectRowIndexes(_:byExtendingSelection:), check for clickedRow != -1 there, set a didChangeSelectionWithMouse flag to true if so, and check that in the selection-did-change delegate method. That works, but it's not the most elegant of solutions. So: Am I misunderstanding the Tech Note? Can currentEvent still in fact be used safely in tableViewSelectionDidChange(_:) in macOS 27 and beyond? If not, what is the recommended way of checking that the table selection has been changed by a mouse click? Many thanks!
Topic: UI Frameworks SubTopic: AppKit Tags:
8
0
421
Jun ’26
How to override NSTextView dragging behaviour without overriding mouseDown:?
I have an NSTextView subclass that overrides mouseDown: to allow for image resizing. If a user clicks and drags on the edges of an image, I implement custom behaviour that resizes the image (and shows resizing cursors). If the user clicks anywhere else, super's implementation is called. This all works great. As of macOS 27, however, the transition to gesture recognisers instead of overriding mouseDown: means that I should probably be moving away from the above approach. NSTextView now uses the new NSTextSelectionManager to implement selection and dragging via gesture recognisers, although, according to the release notes: Existing NSTextView subclasses that override mouseDown: continue to work through a binary-compatible fallback path. (163365571) It's unclear whether this means that we therefore should still override mouseDown: for custom behaviour in NSTextView, but to me, this, along with the content of Tech Note TN3212, strongly implies that, although it will continue to work thanks to "a binary-compatible fallback", we should entirely move away from overriding mouseDown: in the future. If that is indeed the case, how do we implement custom dragging behaviour--such as for resizing images as in my example--in NSTextView? There still seems to be no way of doing it other than overriding mouseDown:. I had thought that I might be able to add an NSPanGestureRecognizer to the text view and have it fail via its delegate methods if the clicks were outside of an image's edges, but a pan gesture recogniser added to an NSTextView is entirely ignored, presumably because of the private gestures already added. Fortunately everything continues to work for now, but I would like to update my code as much as possible.
Topic: UI Frameworks SubTopic: AppKit Tags:
4
0
380
Jun ’26
NSTextView.menuForEvent - getting the affected range on macOS 27
I have an NSTextView subclass that replaces the standard context menu with a custom one by building its own menu in an menuForEvent: override. The context menu that is shown depends on what is selected in the text view, so it checks the current selectedRange to determine which commands should be included. (I could do much the same via the NSTextViewDelegate method textView(_:menu:for:at:), but since my text view subclass is used in several areas, each with their own delegate, it's better to do it in menuForEvent(_:).) This has all worked fine for years, but with NSTextView's transition to gesture recognisers and NSTextSelectionManager for handling text selections in macOS 27, the timings have changed. Previously, if you Ctrl-clicked on a word so that the word is selected and the context menu appears, setSelectedRanges:affinity:stillSelecting: would be called first and then menuForEvent: would be called. On macOS 27, however, the order of events is reversed: menuForEvent: is called first, before setSelectedRanges:affinity:stillSelecting:. (If you implement NSMenuDelegate.menuNeedsUpdate:, that is also called before setSelectedRanges:....) This means that you can no longer rely on selectedRanges in menuForEvent:, because on macOS 27, selectedRanges in menuForEvent: represents the previous selection, not the selection you will actually see on screen when the menu appears. This makes building a custom context menu somewhat tricky. I suspect this is a bug and have reported it as such (FB23251873, with apologies to the engineers for the pleading tone in that report; it was at the end of a long week of getting my head around the gesture recogniser changes. :) ). But it occurs to me that there is nothing in the documentation that guarantees that selectedRanges will be accurate in menuForEvent:; it's just always worked this way. So am I missing something here? Is there a better or more reliable way of getting the range that will be affected by the contextual menu in NSTextView? (The delegate method only provides a single character index, which isn't enough information for spelling corrections and such which require a range.) My current workaround is to rebuild the context menu in setSelectedRanges:affinity:stillSelecting: if it is called between menuForEvent: and didCloseMenu:withEvent:.
Topic: UI Frameworks SubTopic: AppKit Tags:
2
0
310
Jun ’26
FileManager.replaceItemAt(_:withItemAt:) fails sporadically on ubiquitous items
I’m encountering a strange, sporadic error in FileManager.replaceItemAt(_:withItemAt:) when trying to update files that happen to be stored in cloud containers such as iCloud Drive or Dropbox. Here’s my setup: I have an NSDocument-based app which uses a zip file format (although the error can be reproduced using any kind of file). In my NSDocument.writeToURL: implementation, I do the following: Create a temp folder using FileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: fileURL, create: true). Copy the original zip file into the temp directory. Update the zip file in the temp directory. Move the updated zip file into place by moving it from the temp directory to the original location using FileManager.replaceItemAt(_:withItemAt:). This all works perfectly - most of the time. However, very occasionally I receive a save error caused by replaceItemAt(_withItemAt:) failing. Saving can work fine for hundreds of times, but then, once in a while, I’ll receive an “operation not permitted” error in replaceItemAt. I have narrowed the issue down and found that it only occurs when the original file is in a cloud container - when FileManager.isUbiquitousItem(at:) returns true for the original fileURL I am trying to replace. (e.g. Because the user has placed the file in iCloud Drive.) Although strangely, the permissions issue seems to be with the temp file rather than with the original (if I try copying or deleting the temp file after this error occurs, I’m not allowed; I am allowed to delete the original though - not that I’d want to of course). Here’s an example of the error thrown by replaceItemAt: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “Dropbox”." UserInfo={NSFileBackupItemLeftBehindLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFileOriginalItemLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSURL=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSFileNewItemLocationKey=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSUnderlyingError=0xb1e22ff90 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “NSIRD_TempFolderBug_y3UvzP”." UserInfo={NSURL=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFilePath=/var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSUnderlyingError=0xb1e22ffc0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}}} And here’s some very simple sample code that reproduces the issue in a test app: // Ask user to choose this via a save panel. var savingURL: URL? { didSet { setUpSpamSave() } } var spamSaveTimer: Timer? // Set up a timer to save the file every 0.2 seconds so that we can see the sporadic save problem quickly. func setUpSpamSave() { spamSaveTimer?.invalidate() let timer = Timer(fire: Date(), interval: 0.2, repeats: true) { [weak self] _ in self?.spamSave() } spamSaveTimer = timer RunLoop.main.add(timer, forMode: .default) } func spamSave() { guard let savingURL else { return } let fileManager = FileManager.default // Create a new file in a temp folder. guard let replacementDirURL = try? fileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: savingURL, create: true) else { return } let tempURL = replacementDirURL.appendingPathComponent(savingURL.lastPathComponent) guard (try? "Dummy text".write(to: tempURL, atomically: false, encoding: .utf8)) != nil else { return } do { // Use replaceItemAt to safely move the new file into place. _ = try fileManager.replaceItemAt(savingURL, withItemAt: tempURL) print("save succeeded!") try? fileManager.removeItem(at: replacementDirURL) // Clean up. } catch { print("save failed with error: \(error)") // Note: if we try to remove replaceDirURL here or do anything with tempURL we will be refused permission. NSAlert(error: error).runModal() } } If you run this code and set savingURL to a location in a non-cloud container such as your ~/Documents directory, it will run forever, resaving the file over and over again without any problems. But if you run the code and set savingURL to a location in a cloud container, such as in an iCloud Drive folder, it will work fine for a while, but after a few minutes - after maybe 100 saves, maybe 500 - it will throw a permissions error in replaceItemAt. (Note that my real app has all the save code wrapped in file coordination via NSDocument methods, so I don’t believe file coordination to be the problem.) What am I doing wrong here? How do I avoid this error? Thanks in advance for any suggestions.
15
0
514
Mar ’26
UIDocumentViewController missing page background in browser on iPadOS 27
Since iPadOS 18, UIDocumentViewController has contained a document browser that shows a white page with rounded corners against a background of your choice, with the app name and "Create Document" buttons on the page. For instance, when you launch Pages, you see a white rounded page rectangle against a background of swirly orange, with “Choose a Template” and “Start Writing” buttons on the white page. In Numbers, there’s a green swirly background. In apps built and run on iPadOS 27, however, the white page with rounded corners is entirely missing, making the browser screen very ugly, with the “New Document” button translucent directly against whatever background is set. This can be reproduced simply by creating a new iOS "Document App" in Xcode 27 and building on iPadOS 27. I assume this is a bug, since if you turn on exception breakpoints, you see the following exception breakpoint triggered during launch: Exception = (NSException *) "[<_UIDocumentLaunchViewController 0x10732b200> valueForUndefinedKey:]: this class is not key value coding-compliant for the key _pageContainerView." I have thus reported it as FB23418746. I am curious, though, whether it is a design decision to remove the page background on iPadOS 27, or whether I am missing some sort of setting in the UIDocumentViewController’s launch options for restoring the page. (I hope it’s not intentional, as I like the page, and without it, the black app name gets lost against darker or busier backgrounds.) (I did try to include screenshots showing the issue when I first went to post this message, but doing so resulted in my IP address being blocked access to the forums for a week because of the forums’ new security measures.)
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
2
Boosts
0
Views
209
Activity
2w
NSTableView: checking for mouse-driven selection changes on macOS 27
I have an NSTableView used as a source list and, alongside it, two editors. When the user selects anything in the table view, its content is opened in the editor that has the focus. When the user Opt-clicks an item in the table, though, the content is opened in the other editor, making it easy for the user to load something in the other editor without having to change the focus first. This has worked for many years using NSTableView.selectiondDidChange / the NSTableViewDelegate as follows: func tableViewSelectionDidChange(_ notification: Notification) { if let event = tableView.window?.currentEvent, event.type == .leftMouseUp || event.type == .leftMouseDown, // (Real app does some other checks here too.) event.modifierFlags.contains(.option) { openInOtherEditor() return } openInCurrentEditor() } However, on macOS 27, it seems that things need to be done differently because of the transition to gesture recognisers for event handling. According to the WWDC video "Modernise Your AppKit App", and to Tech Note TN3212, currentEvent can no longer be relied upon to provide the event that actually triggered an action in NSControl subclasses: The transition to gesture recognizers on NSControl objects changes the timing of when AppKit delivers control action messages with respect to event processing. As a result, currentEvent no longer returns the event that triggered an action. It's unclear whether this new limitation refers only to NSControl.action or to all mouse-driven actions, but from the context and what the rest of the Tech Note has to say, I assume it's the latter. (Especially since you are no longer supposed to override mouseDown(with:), and the Console warns about gestures being disabled if you do override mouseDown(with:) in an NSTableView subclass on macOS 27.) currentEvent still seems to work fine in this situation in the first macOS 27 beta, but it sounds as though we cannot rely on this continuing to be the case. If we should no longer be using currentEvent, then, what should we use instead to determine whether a selection change was triggered by a mouse click? The Tech Note and WWDC video have nothing to say about this. They simply say that instead of overriding mouseDown(with:), you should use the selection-did-change delegate methods, which is of no help here. (By contrast, checking the modifier flags is still straightforward; the Tech Note says to use NSEvent.modifierFlags instead of currentEvent.modifierFlags.) Two solutions sprung to mind, but neither worked: Check tableView.clickedRow != -1 in the selectionDidChange delegate method/notification response. This doesn't work, however, because clickedRow has been reset to -1 by the time NSTableView.selectionDidChange is sent. Add an action to the table view and check clickedRow there. This doesn't work either, though, because although clickedRow is available in the action method, I would now have to load content in response to both an action and a selection change, and since the selection changes before the action is called, there is no way of telling my selection-did-change method not to load in the main editor if Option is held down in the action. The only solution I have found is to override selectRowIndexes(_:byExtendingSelection:), check for clickedRow != -1 there, set a didChangeSelectionWithMouse flag to true if so, and check that in the selection-did-change delegate method. That works, but it's not the most elegant of solutions. So: Am I misunderstanding the Tech Note? Can currentEvent still in fact be used safely in tableViewSelectionDidChange(_:) in macOS 27 and beyond? If not, what is the recommended way of checking that the table selection has been changed by a mouse click? Many thanks!
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
8
Boosts
0
Views
421
Activity
Jun ’26
How to override NSTextView dragging behaviour without overriding mouseDown:?
I have an NSTextView subclass that overrides mouseDown: to allow for image resizing. If a user clicks and drags on the edges of an image, I implement custom behaviour that resizes the image (and shows resizing cursors). If the user clicks anywhere else, super's implementation is called. This all works great. As of macOS 27, however, the transition to gesture recognisers instead of overriding mouseDown: means that I should probably be moving away from the above approach. NSTextView now uses the new NSTextSelectionManager to implement selection and dragging via gesture recognisers, although, according to the release notes: Existing NSTextView subclasses that override mouseDown: continue to work through a binary-compatible fallback path. (163365571) It's unclear whether this means that we therefore should still override mouseDown: for custom behaviour in NSTextView, but to me, this, along with the content of Tech Note TN3212, strongly implies that, although it will continue to work thanks to "a binary-compatible fallback", we should entirely move away from overriding mouseDown: in the future. If that is indeed the case, how do we implement custom dragging behaviour--such as for resizing images as in my example--in NSTextView? There still seems to be no way of doing it other than overriding mouseDown:. I had thought that I might be able to add an NSPanGestureRecognizer to the text view and have it fail via its delegate methods if the clicks were outside of an image's edges, but a pan gesture recogniser added to an NSTextView is entirely ignored, presumably because of the private gestures already added. Fortunately everything continues to work for now, but I would like to update my code as much as possible.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
4
Boosts
0
Views
380
Activity
Jun ’26
NSTextView.menuForEvent - getting the affected range on macOS 27
I have an NSTextView subclass that replaces the standard context menu with a custom one by building its own menu in an menuForEvent: override. The context menu that is shown depends on what is selected in the text view, so it checks the current selectedRange to determine which commands should be included. (I could do much the same via the NSTextViewDelegate method textView(_:menu:for:at:), but since my text view subclass is used in several areas, each with their own delegate, it's better to do it in menuForEvent(_:).) This has all worked fine for years, but with NSTextView's transition to gesture recognisers and NSTextSelectionManager for handling text selections in macOS 27, the timings have changed. Previously, if you Ctrl-clicked on a word so that the word is selected and the context menu appears, setSelectedRanges:affinity:stillSelecting: would be called first and then menuForEvent: would be called. On macOS 27, however, the order of events is reversed: menuForEvent: is called first, before setSelectedRanges:affinity:stillSelecting:. (If you implement NSMenuDelegate.menuNeedsUpdate:, that is also called before setSelectedRanges:....) This means that you can no longer rely on selectedRanges in menuForEvent:, because on macOS 27, selectedRanges in menuForEvent: represents the previous selection, not the selection you will actually see on screen when the menu appears. This makes building a custom context menu somewhat tricky. I suspect this is a bug and have reported it as such (FB23251873, with apologies to the engineers for the pleading tone in that report; it was at the end of a long week of getting my head around the gesture recogniser changes. :) ). But it occurs to me that there is nothing in the documentation that guarantees that selectedRanges will be accurate in menuForEvent:; it's just always worked this way. So am I missing something here? Is there a better or more reliable way of getting the range that will be affected by the contextual menu in NSTextView? (The delegate method only provides a single character index, which isn't enough information for spelling corrections and such which require a range.) My current workaround is to rebuild the context menu in setSelectedRanges:affinity:stillSelecting: if it is called between menuForEvent: and didCloseMenu:withEvent:.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
2
Boosts
0
Views
310
Activity
Jun ’26
FileManager.replaceItemAt(_:withItemAt:) fails sporadically on ubiquitous items
I’m encountering a strange, sporadic error in FileManager.replaceItemAt(_:withItemAt:) when trying to update files that happen to be stored in cloud containers such as iCloud Drive or Dropbox. Here’s my setup: I have an NSDocument-based app which uses a zip file format (although the error can be reproduced using any kind of file). In my NSDocument.writeToURL: implementation, I do the following: Create a temp folder using FileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: fileURL, create: true). Copy the original zip file into the temp directory. Update the zip file in the temp directory. Move the updated zip file into place by moving it from the temp directory to the original location using FileManager.replaceItemAt(_:withItemAt:). This all works perfectly - most of the time. However, very occasionally I receive a save error caused by replaceItemAt(_withItemAt:) failing. Saving can work fine for hundreds of times, but then, once in a while, I’ll receive an “operation not permitted” error in replaceItemAt. I have narrowed the issue down and found that it only occurs when the original file is in a cloud container - when FileManager.isUbiquitousItem(at:) returns true for the original fileURL I am trying to replace. (e.g. Because the user has placed the file in iCloud Drive.) Although strangely, the permissions issue seems to be with the temp file rather than with the original (if I try copying or deleting the temp file after this error occurs, I’m not allowed; I am allowed to delete the original though - not that I’d want to of course). Here’s an example of the error thrown by replaceItemAt: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “Dropbox”." UserInfo={NSFileBackupItemLeftBehindLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFileOriginalItemLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSURL=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSFileNewItemLocationKey=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSUnderlyingError=0xb1e22ff90 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “NSIRD_TempFolderBug_y3UvzP”." UserInfo={NSURL=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFilePath=/var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSUnderlyingError=0xb1e22ffc0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}}} And here’s some very simple sample code that reproduces the issue in a test app: // Ask user to choose this via a save panel. var savingURL: URL? { didSet { setUpSpamSave() } } var spamSaveTimer: Timer? // Set up a timer to save the file every 0.2 seconds so that we can see the sporadic save problem quickly. func setUpSpamSave() { spamSaveTimer?.invalidate() let timer = Timer(fire: Date(), interval: 0.2, repeats: true) { [weak self] _ in self?.spamSave() } spamSaveTimer = timer RunLoop.main.add(timer, forMode: .default) } func spamSave() { guard let savingURL else { return } let fileManager = FileManager.default // Create a new file in a temp folder. guard let replacementDirURL = try? fileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: savingURL, create: true) else { return } let tempURL = replacementDirURL.appendingPathComponent(savingURL.lastPathComponent) guard (try? "Dummy text".write(to: tempURL, atomically: false, encoding: .utf8)) != nil else { return } do { // Use replaceItemAt to safely move the new file into place. _ = try fileManager.replaceItemAt(savingURL, withItemAt: tempURL) print("save succeeded!") try? fileManager.removeItem(at: replacementDirURL) // Clean up. } catch { print("save failed with error: \(error)") // Note: if we try to remove replaceDirURL here or do anything with tempURL we will be refused permission. NSAlert(error: error).runModal() } } If you run this code and set savingURL to a location in a non-cloud container such as your ~/Documents directory, it will run forever, resaving the file over and over again without any problems. But if you run the code and set savingURL to a location in a cloud container, such as in an iCloud Drive folder, it will work fine for a while, but after a few minutes - after maybe 100 saves, maybe 500 - it will throw a permissions error in replaceItemAt. (Note that my real app has all the save code wrapped in file coordination via NSDocument methods, so I don’t believe file coordination to be the problem.) What am I doing wrong here? How do I avoid this error? Thanks in advance for any suggestions.
Replies
15
Boosts
0
Views
514
Activity
Mar ’26