I have a C function called Foo_C_Func, which I need to use as a callback. The rest of the app is coded in Swift. As I understand it, the following code should work, but instead I get a compiler error.
// typealias makes our function signature easier to read
typealias Sig = ( EventHandlerCallRef, EventRef, UnsafeMutablePointer<Void>) -> OSStatus
// We need to make a CFunctionPointer to the C function
var ptr = UnsafeMutablePointer<Sig>.alloc(1)
ptr.initialize( Foo_C_Func )
let c_ptr = COpaquePointer( ptr )
let proc_ptr = CFunctionPointer<Sig>( c_ptr ) as EventHandlerProcPtr
// now we should be able to create the EventHandlerUPP
let handler_upp = NewEventHandlerUPP( proc_ptr )
Trying to Build this fails with the following error:
Undefined symbols for architecture x86_64:
"_Foo_C_Func", referenced from:
__TTOFSC10Foo_C_FuncFTVSs14COpaquePointerS_GVSs20UnsafeMutablePointerT___VSs5Int32 in Demo.o
"_NewEventHandlerUPP", referenced from:
__TFC17DemocfMS0_FT_S0_ in Demo.o
ld: symbol(s) not found for architecture x86_64
I also notice when I hover over the last line, that the Xcode tooltip shows the return type of NewEventHandlerUpp to be (EventHandlerProcPtr) rather than EventHandlerProcPtr.
Am I doing it wrong, or is it impossible to create an EventHandlerUPP within Swift?
I'm posting this for user MAH, though I have abandoned the original project. The following code compiles in Swift 3. I haven't check whether it works beyond that.
import Carbon
func hotkey_callback( _:EventHandlerCallRef?, _ event:EventRef?, _ context:UnsafeMutablePointer<Void>? ) -> OSStatus {
// Do stuff
return noErr
}
let handler_ref_ptr = UnsafeMutablePointer<EventHandlerRef?>( allocatingCapacity: 1 )
let spec_ptr = UnsafeMutablePointer<EventTypeSpec>( allocatingCapacity: 1 )
spec_ptr.pointee = EventTypeSpec( eventClass: OSType( kEventClassKeyboard ), eventKind: UInt32( kEventHotKeyPressed ) )
let hotkey_callback_pointer = hotkey_callback as! EventHandlerUPP
let status = InstallEventHandler( GetEventDispatcherTarget(), hotkey_callback_pointer, 1, spec_ptr, nil, handler_ref_ptr )
Here's an alternate answer, if you're using Swift 3. Swift 2 is different than Swift 3, in that the former sucks, whereas the latter is usable.
let hotkey_callback: EventHandlerUPP = { _, _, _ in
// Do stuff
return noErr
}
var handler: EventHandlerRef? = nil
var spec = EventTypeSpec(
eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)
)
let status = InstallEventHandler(
GetEventDispatcherTarget(), hotkey_callback, 1, &spec, nil, &handler
)
Note that we can use "&" to avoid manually allocating pointers now, and note that adhering to the informal "EventHandlerUPP" protocol is now explicit and doesn't require our manually declaring the Type of each argument. So much easier to read :)
Related
I previously used this code in Swift 4.2 to generate an id:
public static func generateId() throws -> UInt32 {
let data: Data = try random(bytes: 4)
let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
return value // + some other stuff
}
withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }
(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.
Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:
let randomId = UInt32.random(in: .min ... .max)
On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.
Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.
Example reading from the pointer buffer (code is unrelated to the question):
var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
return
}
reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}
Example writing to the buffer pointer (code is unrelated to the question):
try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
passphrase,
passphrase.utf8.count,
salt,
salt.utf8.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
rounds,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
kCCKeySizeAES256)
guard status == kCCSuccess else {
throw Error.keyDerivationError
}
}
The code from the question would look like:
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}
In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.
One more way to fix this warning to use bindMemory(to:).
var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
return Int32(kCCMemoryFailure)
}
return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer
Original code from a tutorial I was working on.
--> where input: Data
--> where stream: compression_stream
//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
//holder
var output = Data()
//Source and destination buffers
stream.src_ptr = srcPointer //UnsafePointer<UInt8>
stream.src_size = input.count
… etc.
}
Code with a conversion to make the above code work with a valid method
return input.withUnsafeBytes { bufferPtr in
//holder
var output = Data()
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
return output
}
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
// Jump back into the original method
stream.src_ptr = typedPointer //UnsafePointer<UInt8>
}
I previously used this code in Swift 4.2 to generate an id:
public static func generateId() throws -> UInt32 {
let data: Data = try random(bytes: 4)
let value: UInt32 = data.withUnsafeBytes { $0.pointee } // deprecated warning!
return value // + some other stuff
}
withUnsafeBytes is deprecated on Swift 5.0. How can I solve this?
In Swift 5 the withUnsafeBytes() method of Data calls the closure with an (untyped) UnsafeRawBufferPointer, and you can load() the value from the raw memory:
let value = data.withUnsafeBytes { $0.load(as: UInt32.self) }
(compare How to use Data.withUnsafeBytes in a well-defined manner? in the Swift forum). Note that this requires that the memory is aligned on a 4-byte boundary. For alternatives see round trip Swift number types to/from Data.
Note also that as of Swift 4.2 you can create a random 32-bit integer simply using the new Random API:
let randomId = UInt32.random(in: .min ... .max)
On Xcode 10.2, Swift 5, using $0.load(as:) didn't work for me, both when reading from the pointer or writing to it.
Instead, using $0.baseAddress?.assumingMemoryBound(to:) seems to work well.
Example reading from the pointer buffer (code is unrelated to the question):
var reachability: SCNetworkReachability?
data.withUnsafeBytes { ptr in
guard let bytes = ptr.baseAddress?.assumingMemoryBound(to: Int8.self) else {
return
}
reachability = SCNetworkReachabilityCreateWithName(nil, bytes)
}
Example writing to the buffer pointer (code is unrelated to the question):
try outputData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) in
let status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
passphrase,
passphrase.utf8.count,
salt,
salt.utf8.count,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1),
rounds,
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
kCCKeySizeAES256)
guard status == kCCSuccess else {
throw Error.keyDerivationError
}
}
The code from the question would look like:
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt32.self)
}
In cases where the 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(…) warning persists, it seems like the compiler can get confused when the closure has only one line. Making the closure have two or more lines might remove the ambiguity.
One more way to fix this warning to use bindMemory(to:).
var rawKey = Data(count: rawKeyLength)
let status = rawKey.withUnsafeMutableBytes { rawBytes -> Int32 in
guard let rawBytes = rawBytes.bindMemory(to: UInt8.self).baseAddress else {
return Int32(kCCMemoryFailure)
}
return CCSymmetricKeyUnwrap(alg, ivBytes, iv.count, keyBytes, key.count, wrappedKeyBytes, wrappedKey.count, rawBytes, &rawKeyLength)
}
I got this error as I was trying to figure out a compression stream tutorial. To get it to work, I added a step of converting the raw buffer pointer to a UnsafePointer
Original code from a tutorial I was working on.
--> where input: Data
--> where stream: compression_stream
//Method that shows the deprecation alert
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
//holder
var output = Data()
//Source and destination buffers
stream.src_ptr = srcPointer //UnsafePointer<UInt8>
stream.src_size = input.count
… etc.
}
Code with a conversion to make the above code work with a valid method
return input.withUnsafeBytes { bufferPtr in
//holder
var output = Data()
//Get the Raw pointer at the initial position of the UnsafeRawBuffer
let base: UnsafeRawPointer? = bufferPtr.baseAddress
//Unwrap (Can be combined with above, but kept it separate for clarity)
guard let srcPointer = base else {
return output
}
//Bind the memory to the type
let count = bufferPtr.count
let typedPointer: UnsafePointer<UInt8> = srcPointer.bindMemory(to: UInt8.self, capacity: count)
// Jump back into the original method
stream.src_ptr = typedPointer //UnsafePointer<UInt8>
}
I'm having trouble exploring the results of a Spotlight search in Swift 3 using MDQuery. I expect MDQueryGetResultAtIndex to yield an MDItem, and in C/Objective-C, that assumption works, and I can call MDItemCopyAttribute on it to explore the item. Here, for example, I successfully get the pathname of a found item:
MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(q,i);
CFStringRef path = MDItemCopyAttribute(item,kMDItemPath);
But in Swift 3, MDQueryGetResultAtIndex returns an UnsafeRawPointer! (it's a pointer-to-void in C). To get past that, I've tried, for example:
if let item = MDQueryGetResultAtIndex(q, 0) {
let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}
but that crashes, and logging shows that ptr.pointee is an NSAtom. It is quite evident that my personal UnsafeRawPointer mojo is not working (and to be frank I've always found this confusing).
How would I transform this UnsafeRawPointer into something I can successfully call MDItemCopyAttribute on?
Alternatives
I can get over this hump by putting my Objective-C code into an Objective-C helper object and calling it from Swift; but I'd like to write a pure Swift solution.
Similarly, I could probably rewrite my code to use the higher-level NSMetadataQuery, and I may well do so; but my original Objective-C code using the lower-level MDQueryRef works fine, so now I'm curious how to turn it directly into Swift.
Complete code for those who would like to try this at home:
let s = "kMDItemDisplayName == \"test\"" // you probably have one somewhere
let q = MDQueryCreate(nil, s as CFString, nil, nil)
MDQueryExecute(q, CFOptionFlags(kMDQuerySynchronous.rawValue))
let ct = MDQueryGetResultCount(q)
if ct > 0 {
if let item = MDQueryGetResultAtIndex(q, 0) {
// ...
}
}
The problem in your code
if let item = MDQueryGetResultAtIndex(q, 0) {
let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}
is that the UnsafeRawPointer is interpreted as a pointer to an
MDItem reference and then dereferenced in ptr.pointee, but
the raw pointer is the MDItem reference, so it is dereferenced
once too often.
The "shortest" method to convert the raw pointer to an MDItem reference
is unsafeBitCast:
let item = unsafeBitCast(rawPtr, to: MDItem.self)
which is the direct analogue of an (Objective-)C cast.
You can also use the Unmanaged methods
to convert the raw pointer to an unmanaged reference and from there
to a managed reference (compare How to cast self to UnsafeMutablePointer<Void> type in swift):
let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
This looks a bit more complicated but perhaps expresses the intention
more clearly. The latter approach works also with (+1) retained
references (using takeRetainedValue()).
Self-contained example:
import CoreServices
let queryString = "kMDItemContentType = com.apple.application-bundle"
if let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil) {
MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))
for i in 0..<MDQueryGetResultCount(query) {
if let rawPtr = MDQueryGetResultAtIndex(query, i) {
let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
print(path)
}
}
}
}
Trying to get printable rect for OS X app. Seems to involve creating a session, then a page format, validating the format, etc. Code compiles, but getting a status of -50 from PMCreateSession. Am I declaring printSession improperly? Normally don't have to deal so much with UnsafeMutablePointers.
Thanks!
let printSession: UnsafeMutablePointer<PMPrintSession> = nil
let pmPageFormat: UnsafeMutablePointer<PMPageFormat> = nil
var status = PMCreateSession(printSession)
status = PMCreatePageFormat(pmPageFormat)
status = PMSessionDefaultPageFormat(printSession.memory, pmPageFormat.memory)
let changed: UnsafeMutablePointer<DarwinBoolean> = nil
status = PMSessionValidatePageFormat(printSession.memory, pmPageFormat.memory, changed)
changed.destroy()
var pRect = PMRect()
status = PMGetAdjustedPageRect(pmPageFormat.memory, &pRect)
Swift.print("pRect \(pRect)")
status = PMRelease(pmPageFormat)
status = PMRelease(printSession)
Am I declaring printSession improperly?
One could acquire a PMPrintSession as follows:
// create a C Null pointer of type PMPrintSession
let printSession = unsafeBitCast(0, to: PMPrintSession.self)
// pass by & converts PMPrintSession to UnsafeMutablePointer<PMPrintSession>
PMCreateSession(&printSession)
…
// recast printSession to release memory
PMRelease( PMObject(printSession) )
Alternately, a PMPrintSession can be accessed from the Cocoa NSPrintInfo:
let printInfo = NSPrintInfo.shared()
let printSession = PMPrintSession(printInfo.pmPrintSession())
Normally don't have to deal so much with UnsafeMutablePointers
For information more information on using an UnsafeMutablePointer see StackOverflow "How to use UnsafeMutablePointer in Swift?"
For a complete example of using Core Printing with Swift see 004.42Apple_CorePrintingExample on GitHub.
General question: I have an object which I want to convert to a ConstUnsafePointer. Everything I've tried seems to fail...how do I do it? I was able to do this previously in DP2, but cannot work it out in DP3.
Previously, I had code which looked like this:
var bpData = AUSamplerBankPresetData(bankURL: Unmanaged<CFURL>(_private: soundBankURL), bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), presetID: presetID, reserved: 0)
let bpDataPointer: CConstVoidPointer = &bpData
// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(samplerAudioUnit,
UInt32(kAUSamplerProperty_LoadPresetFromBank),
UInt32(kAudioUnitScope_Global),
0, bpDataPointer, 8)
However, now CConstVoidPointer is no longer around and has been replaced by ConstUnsafePointer. The AudioUnitSetProperty method looks like this:
func AudioUnitSetProperty(inUnit: AudioUnit, inID: AudioUnitPropertyID, inScope: AudioUnitScope, inElement: AudioUnitElement, inData: ConstUnsafePointer<()>, inDataSize: UInt32) -> OSStatus
But, if I change CConstVoidPointer to ConstUnsafePointer<()> I get the error:
'inout AUSamplerBankPresetData' is not convertible to 'ConstUnsafePointer<()>'
Anyone have any ideas? Thanks.
It doesn't appear necessary to have the intermediate pointer constant. You should be able to do this:
result = AudioUnitSetProperty(
samplerAudioUnit,
UInt32(kAUSamplerProperty_LoadPresetFromBank),
UInt32(kAudioUnitScope_Global),
0,
&bpData,
8)
The func has this definition:
func AudioUnitSetProperty(
inUnit: AudioUnit,
inID: AudioUnitPropertyID,
inScope: AudioUnitScope,
inElement: AudioUnitElement,
inData: ConstUnsafePointer<()>,
inDataSize: UInt32
) -> OSStatus
This line:
inData: ConstUnsafePointer<()>,
Is translated from this C:
const void * inData,
The inData parameter is a constant pointer to anything, which is what ConstUnsafePointer<()> means. I could not try this exact code within a Core Audio app, but I have gotten similar code to work.
In beta 6, it's changed again. Thankfully, this time it looks like the solution is here to stay and doesn't involve any C.
let description = AudioComponentDescription(componentType: OSType(kAudioUnitType_MusicDevice),
componentSubType: OSType(kAudioUnitSubType_Sampler),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,
componentFlagsMask: 0)
let instrument = AVAudioUnitSampler(audioComponentDescription: description)
let soundBankPath = NSBundle.mainBundle().pathForResource(fileName, ofType: "sf2")
let soundBankURL = NSURL.fileURLWithPath(soundBankPath!)
var error: NSError? = nil
instrument.loadSoundBankInstrumentAtURL(soundBankURL, program: program, bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB), bankLSB: UInt8(kAUSampler_DefaultBankLSB), error: &error)
For my sf2 files, the program is usually 1 or 0. I'm not really sure what this refers to, and any help is appreciated. However, this should get you unblocked for your program with a little trial and error.