Asynchronous File I/O in Xojo with DispatchIOMBS

The MBS Xojo MacOSX Plugin 25.3 introduces a powerful new class: DispatchIOMBS. This macOS and iOS-only class provides asynchronous I/O operations using Apple’s Dispatch I/O API. Whether you want to stream or randomly access file data, DispatchIOMBS gives you fine control over reads and writes—with event-driven or delegate-based callbacks.

Let’s explore how to use DispatchIOMBS to copy a file asynchronously.

Why use dispatch I/O?

  • It runs I/O in the background, avoiding UI freezes.
  • Built-in memory management avoids unnecessary copying.
  • It monitors system pressure for optimized performance.
  • Supports stream (kTypeStream) or random-access (kTypeRandom) modes.
  • We got a file copy example for Xojo for you

Our example file copies a large file from one location to another asynchronously using stream mode. It uses event handlers rather than delegates for simplicity.

But let’s start with a simple async read operation:

Sub Opening()
	const path = "/Users/cs/Pictures/Eisblume.jpeg"
	Var InputFile As New FolderItem(path, FolderItem.PathModes.Native)
	
	Var DispatchIORead As New DispatchIOMBS(DispatchIOMBS.kTypeStream, InputFile, 0)
	
	DispatchIORead.Read(0, 10000000000000, AddressOf ReadCompleted)
End EventHandler

As you see we open a file for reading and request the whole file to be read by passing a huge maximum size. Alternatively you can of course pass InputFile.length here. Then we get the delegate called multiple times with chunks:

Sub ReadCompleted(DispatchIO As DispatchIOMBS, offset As Int64, data As DispatchDataMBS, done As Boolean, error As Integer)
	System.DebugLog CurrentMethodName
	System.DebugLog "offset: "+offset.ToString
	System.DebugLog "data len: " +data.Size.ToString
	System.DebugLog "done: "+done.ToString
	
	Break
End Sub

We log the calls and see we get three calls for a 1.3 MB big file. First call gives 1 MB, then second call provides the rest and last call sets the done flag.

Test Launched

Window1.ReadCompleted
offset: 0
data len: 1048576
done: False

Window1.ReadCompleted
offset: 0
data len: 253015
done: False

Window1.ReadCompleted
offset: 0
data len: 0
done: True

Test Ended

While we read the file in the background, the application do other things in-between.

e.g. you could have the user pick a file and start reading it. Then the user could enter maybe some metadata or set some options while the file is read in the background.

Check out the example file coming with the plugin to copy. Compared to using binarystream, you can have read and write not blocking the main thread and actually have the application doing all at the same time: read, write and user interface.

2 Likes

That looks nice, but does this alternative provide the same methods as the BinaryStream class, like ReadInt8, ReadPString, etc?

Any reason you used the huge number (10000000000000) in the example rather than the file length? What’s the benefit?

Is it guaranteed that the done flag will always be set in a last, separate, event where no more data has been processed? (that sounds counter-intuitive to me, BTW).

Overall, thank you for these additions!

Well, it doesn’t have invidiual functions to read bytes. You’d better read a million byte as otherwise the async overhead is not worth it.

The huge number is from one of the examples. Could just be file.length instead.

I am not sure if you always get done flag in another call. We forward the calls.as they come.

The block size can be altered with LowWater/HighWater properties.

Makes sense.

Thanks for your answers, and this new class.