I am a little bit guessing and cannot actually test the code at the moment, but here are my thoughts:
The MIDIThruConnectionFind()
function is declared in MIDIThruConnection.h as
extern OSStatus
MIDIThruConnectionFind( CFStringRef inPersistentOwnerID,
CFDataRef __nonnull * __nonnull outConnectionList )
and therefore imported to Swift as
public func MIDIThruConnectionFind(_ inPersistentOwnerID: CFString,
_ outConnectionList: UnsafeMutablePointer<Unmanaged<CFData>>) -> OSStatus
which means that the last parameter must be the address of an (initialized and) non-optional Unmanaged<CFData>
value.
But that makes no sense: The data is allocated by the function, and we don't want to pass any data in. I strongly assume that this is a bug in the nullability annotation of that function in the C header. Other Core MIDI functions with an out parameter are correctly annotated, e.g.
extern OSStatus
MIDIObjectGetStringProperty( MIDIObjectRef obj,
CFStringRef propertyID,
CFStringRef __nullable * __nonnull str )
The following workaround might work: Declare connectionRef
as an optional pointer (so that it is initialized as nil
), and “cast” it to a non-optional pointer when calling the function:
var connectionRef: Unmanaged<CFData>?
let status = withUnsafeMutablePointer(to: &connectionRef) {
$0.withMemoryRebound(to: Unmanaged<CFData>.self, capacity: 1) {
MIDIThruConnectionFind("" as CFString, $0)
}
}
If that succeeds, the optional pointer can be unwrapped, and the CFData
reference obtained with takeRetainedValue()
. CFData
is toll-free bridged to NSData
, and that can be cast to the Swift overlay type Data
:
if status == noErr, let connectionRef = connectionRef {
let data = connectionRef.takeRetainedValue() as Data
}
Another workaround is to define a wrapper function with the correct nullability annotations in the bridging header file:
#include <CoreMIDI/CoreMIDI.h>
static OSStatus myMIDIThruConnectionFind(CFStringRef inPersistentOwnerID,
CFDataRef __nullable * __nonnull outConnectionList) {
return MIDIThruConnectionFind(inPersistentOwnerID, outConnectionList);
}
which can then be called as
var connectionRef: Unmanaged<CFData>?
let status = myMIDIThruConnectionFind("" as CFString, &connectionRef)