In the realm of cross-platform development, handling file access consistently across different operating systems can be a challenging task. For Xojo developers working on macOS or iOS, NSFileCoordinatorMBS and NSFilePresenterMBS from the Monkeybread Software’s Xojo plugins offer a powerful way to manage file access while avoiding conflicts with iCloud. In this blog post, we’ll delve into a practical example of using these classes in a Xojo project.
Introduction to NSFileCoordinatorMBS and NSFilePresenterMBS
Before we dive into the code, let’s briefly understand what NSFileCoordinatorMBS and NSFilePresenterMBS do:
- NSFileCoordinatorMBS: This class helps in coordinating access to files across different processes. It ensures that multiple processes can read from or write to a file without interfering with each other, thus avoiding file access conflicts.
- NSFilePresenterMBS: This class works in tandem with NSFileCoordinatorMBS and represents a file or directory that presents its state to the file coordinator. It allows your app to be notified of changes to the file or directory it is coordinating. We won’t really use this class today.
Example Scenario
Let’s walk through a simple Xojo application example where a user selects a file, and the application reads its contents. We will use NSFileCoordinatorMBS and NSFilePresenterMBS to manage file access on macOS or iOS while skipping these steps on Windows and Linux.
Sample Code Explained
Here’s a breakdown of the code provided:
Class MainWindow Inherits DesktopWindow
Control OpenButton Inherits DesktopButton
ControlInstance OpenButton Inherits DesktopButton
EventHandler Sub Pressed()
var ofd as new OpenFileDialog
var f as FolderItem = ofd.ShowModal(Self)
if f = nil then return
#if TargetMacOS or TargetIOS then
fp = new NSFilePresenterMBS
fc = new NSFileCoordinatorMBS(fp)
AddHandler fc.coordinateReadingItemAtURL, AddressOf coordinateReadingItemAtURL
// don't ask other apps to save change
var error as NSErrorMBS
var flags as integer = NSFileCoordinatorMBS.NSFileCoordinatorReadingWithoutChanges
fc.coordinateReadingItemAtURL(f, flags, error)
if error <> nil then
MessageBox error.localizedDescription
end if
#else
// Windows and Linux skip this
ReadTheFile file
#endif
End EventHandler
End Control
Sub ReadTheFile(file as FolderItem)
var b as BinaryStream = BinaryStream.Open(file)
var Data as string = b.Read(b.Length)
MessageBox "Read file with "+data.Bytes.ToString+" bytes"
End Sub
Sub coordinateReadingItemAtURL(N as NSFileCoordinatorMBS, url as string, file as folderitem, tag as Variant)
// we got access
ReadTheFile file
End Sub
Property fc As NSFileCoordinatorMBS
Property fp As NSFilePresenterMBS
End Class
1. Setting Up the Open File Dialog
The OpenButton control’s Pressed event handler is triggered when the user clicks the button:
var ofd as new OpenFileDialog
var f as FolderItem = ofd.ShowModal(Self)
Here, we present the user with an OpenFileDialog to select a file. If the user cancels the dialog, f will be nil, and the method exits.
2. Conditional Code for macOS/iOS
For macOS and iOS, we initialize NSFilePresenterMBS and NSFileCoordinatorMBS. We connect the coordinateReadingItemAtURL event with out local method and ask the system to coordinate reading and notify us with an error if this fails or later call the callback method.
#if TargetMacOS or TargetIOS then
fp = new NSFilePresenterMBS
fc = new NSFileCoordinatorMBS(fp)
AddHandler fc.coordinateReadingItemAtURL, AddressOf coordinateReadingItemAtURL
var error as NSErrorMBS
var flags as integer = NSFileCoordinatorMBS.NSFileCoordinatorReadingWithoutChanges
fc.coordinateReadingItemAtURL(f, flags, error)
if error <> nil then
MessageBox error.localizedDescription
end if
#else
ReadTheFile f
#endif
You could subclass NSFileCoordinatorMBS instead of using addHandler if you prefer that of course. But the advance of using delegate, is that you don’t need to subclass each time you call it and just connect the event in code to som method.
While the sample code uses properties to store fp and fc, this is not really necessary. The coordinateReadingItemAtURL method retains the fc object reference and passes it to the delegate method. Once done, this reference gets released automatically.
3. Reading the File
If there are no errors during coordination, the coordinateReadingItemAtURL method is called, which then proceeds to read the file:
Sub ReadTheFile(file as FolderItem)
var b as BinaryStream = BinaryStream.Open(file)
var Data as string = b.Read(b.Length)
MessageBox "Read file with "+data.Bytes.ToString+" bytes"
End Sub
This method reads the contents of the file and displays a message box with the number of bytes read. We call this directly for Windows and Linux since we don’t coordinate there.
4. Handling File Access Coordination Completion
The coordinateReadingItemAtURL method confirms that access to the file has been coordinated and then calls ReadTheFile to perform the actual file reading.
Conclusion
Using NSFileCoordinatorMBS and NSFilePresenterMBS ensures that your Xojo application handles file access gracefully, particularly on macOS and iOS where multiple processes might interact with the same files. This approach helps in avoiding file access conflicts and ensures a smooth user experience. On Windows and Linux, the code simply reads the file without the added complexity of file coordination.
By incorporating these classes, Xojo developers can create robust cross-platform applications that handle file operations consistently and effectively.