I'm currently converting some Objective-C code to swift and I am stuck.
I do have an internal API which fills a CFPropertyList and give back it's format.
MyFunction(CFPropertyListRef list, CFPropertyListFormat *fmt);
In Objective-C I'm calling it via
CFDictionaryRef myList;
CFPropertyListFormat fmt;
MyFunction(&myList, &fmt)
Via the "Generated Interface" i can see that swift converted to
MyFunction(_ list: CFPropertyList!, _ fmt: UnsafeMutablePointer<CFPropertyListFormat>))
When I'm trying to call the function in swift via
var fmt = CFPropertyListFormat.XMLFormat_v1_0
var plist = NSDictionary() as CFPropertyListRef
MyFunction(plist, &fmt)
I'm getting a EXC_BAD_ACCESS.
As the compiler doesn't complain about the types I think this should be right.
Any help is very appreciated!
Thanks
If your usage in Objective-C is really right, the function header of your MyFunction needs to be something like this:
extern void MyFunction(CFPropertyListRef *list, CFPropertyListFormat *fmt);
(CFPropertyListRef actually is just a typealias of void * in Objective-C/C. So, the compiler rarely outputs warnings for many possible type mis-uses.)
And Swift 2 imports such function as:
public func MyFunction(list: UnsafeMutablePointer<Unmanaged<CFPropertyList>?>, _ fmt: UnsafeMutablePointer<CFPropertyListFormat>)
So, assuming you have changed the function header as above,
you need to use it as follows:
var fmt: CFPropertyListFormat = .XMLFormat_v1_0
var umPlist: Unmanaged<CFPropertyList>? = nil
MyFunction(&umPlist, &fmt)
var plist = umPlist?.takeRetainedValue() //or this may be `umPlist?.takeUnretainedValue()`
Related
I have some Swift 3 code to decode an iOS Objective-C protocol (which has a Swift counterpart). After concluding Swift 3 reflection was not developed enough to accomplish what I needed, I stumbled on the objc runtime method protocol_copyMethodDescriptionList(), which returns an array of the following C structs:
struct objc_method_description
SEL name;
char *types;
};
The code fetches a list of protocol selector names, but not sure what's being returned in the type field. I'm confused about how to properly decode objc_method_description.type values.
What I'm getting in the the type fields are in a cryptic format, for example, "B48#0:8#16{_NSRange=QQ}24#40" At first I thought it was a problem in how I was converting C strings, but after some study, I suspect it actually is an encoding of the parameters, similar how Java's JVM passes around method signatures. But I still have no reference by which to decode it.
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var noteView : UITextView!
func decodeProto() {
var methodCount : UInt32 = 1
if let methodList = protocol_copyMethodDescriptionList(UITextViewDelegate.self,
false, true, UnsafeMutablePointer<UInt32>(&methodCount)) {
for i in 0..<Int(methodCount) {
let methodDesc = methodList[i];
let name = methodDesc.name
let types = String(validatingUTF8: methodDesc.types)
print("\(name) \(types)")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
decodeProto()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The output of that in the XCode console is:
Optional(textViewDidBeginEditing:) Optional("v24#0:8#16")
Optional(textViewDidEndEditing:) Optional("v24#0:8#16")
Optional(textViewShouldBeginEditing:) Optional("B24#0:8#16")
Optional(textViewShouldEndEditing:) Optional("B24#0:8#16")
Optional(textView:shouldChangeTextInRange:replacementText:) Optional("B48#0:8#16{_NSRange=QQ}24#40")
Optional(textView:shouldChangeTextInRange:replacementText:) Optional("B48#0:8#16{_NSRange=QQ}24#40")
.
.
.
What is returned objc_method_description.type field?
A method signature encoding scheme
... which includes an obsolete stack offset representation
In other words, to get a usable method signature encoding from the type field, just just extract the alpha-symbolic characters in left-to-right order discarding the digits.
Supporting documentation:
Objective-C Method Signature Type Encoding
Meaning of the numeric elements in signature encoding, explained by an Apple developer
Playing around with Type Encodings
class_addmethod (according to #Richard_J_Ross, this is the root of it)
Objective-C Runtime Programming Guide
Objective-C Runtime Reference
I want to use a Objective-C class in my Swift project and have imported the files and Xcode created the bridge header file and everything is cool... except:
The Objective-C class defines a callback type for a function
typedef void (^SSScanManagerCallback)(BOOL success, NSError *error, NSArray *scannedURLs);
And uses the type in the function declaration
- (void)scanSync:(SSScanManagerCallback)callback; // Synchronous scan.
The class in question is the following: https://github.com/counsyl/scanstream/blob/master/ScanStream/SSScanManager.h#L16
If I then want to use the class in Swift:
let scanManager = SSScanManager();
scanManager.scanSync({(_ success: Bool, _ error: Error, _ scannedURLs: [Any]) -> Void in
if !success {
// ...
}
});
I get the following error:
Cannot convert value of type '(Bool, Error, [Any]) -> Void' to expected argument type 'SSScanManagerCallback!'
Update: Even if I try to set the argument type like so:
scanManager.scanSync({(_ justATry: SSScanManagerCallback!) -> Void in
});
I get the error:
Cannot convert value of type '(SSScanManagerCallback!) -> Void' to expected argument type 'SSScanManagerCallback!'
But how would I set the type to just 'SSScanManagerCallback!' as requested in the error message?
Interestingly, it appears that Swift (tested with 3.0.2) now imports Objective-C block argument types without any nullability annotations as strong optionals (previously they were imported as implicitly unwrapped optionals). I can't seem to find the documentation for this change though.
So in your case, the correct signature is:
scanManager.scanSync {(success: Bool, error: Error?, scannedURLs: [Any]?) -> Void in
// ...
}
But never write it like this, always let Swift infer the argument types where it can, it solves these kinds of type-mismatch problems for you.
scanManager.scanSync { success, error, scannedURLs in
// ...
}
Now you can ⌥ click on the closure arguments and Xcode will tell you the type that Swift infers them to be.
I found an example for IOKit:
var notification:io_object_t
let matching:NSDictionary = IOServiceNameMatching("IODisplayWrangler").takeRetainedValue()
let displayWrangler = IOServiceGetMatchingService(kIOMasterPortDefault, matching)
let notificationPort = IONotificationPortCreate(kIOMasterPortDefault)
IOServiceAddInterestNotification(notificationPort, displayWrangler, kIOGeneralInterest, displayPowerNotificationsCallback, nil, ¬ification)
CFRunLoopAddSource (CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode);
IOObjectRelease (displayWrangler);
The above example is clear to me - so far. But IOServiceAddInteresNotification wants a callback function. In it's simple to do this, by implementing the C-Style function somewhere in the .m-file.
The documentation says that I have to use a callback of type IOServiceInterestCallback.
In C-Style it is defined as follows:
typedef void ( *IOServiceInterestCallback)( void *refcon, io_service_t service, uint32_t messageType, void *messageArgument );
And on objC everything seems to work out perfectly.
What is the equivalent solution in swift? How do I declare the callback function without creating a C or objC file for this?
Any ideas?
Cheers,
Jack
You cannot create C function like callbacks in Swift as closures are not compatible with CFunctionPointer. You can implement some workaround in Objective-C or C. Example is describe in Objective-C Wrapper for CFunctionPointer to a Swift Closure
It may be that this is actually not possible currently, which would be unfortunate. I'm trying to call the CoreMIDI API to set up a MIDI input. This is what I'm trying to do in Swift:
var midiClient = MIDIClientRef()
var inputPort = MIDIEndpointRef()
var status: OSStatus
func readProc(packetList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void {
}
status = MIDIClientCreate("MIDI client", nil, nil, &midiClient);
status = MIDIDestinationCreate(midiClient, "MIDI input", readProc, nil, &inputPort);
But I get this error: '(UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) -> Void' is not convertible to 'MIDIReadProc'
MIDIReadProc's typedef is the following:
typealias MIDIReadProc = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void)>
Is there a way to get a function pointer for my readProc method to pass to the MIDIDestinationCreate API?
In Swift 2.0 (as part of Xcode 7), C APIs that deal in function pointers use function types that are annotated #convention(c). You can pass any Swift function, method, or closure as a #convention(c) function type — but only if that closure conforms to C conventions... e.g. it can't capture state from its surrounding scope.
For details, see Type Attributes in The Swift Programming Language.
As for what's in Xcode 6: Swift 1.x doesn't have a way to convert a Swift function or closure to a C function pointer -- the sole use of the CFunctionPointer type is to pass function pointers imported from (Obj)C APIs to other (Obj)C APIs.
You can declare a function pointer in C code that you expose to Swift via your project's bridging header, then use Swift to pass that to CoreMIDI. But since you're going to be reaching across a bridge anyway, you might instead think about which parts of your project are best to keep in C and what the best interface is from those parts to your Swift code is.
Swift 1.x (Old Way)
There's a way to do that - Objective-C Runtime is the trick.
import CoreMIDI
let block : #objc_block
(UnsafePointer<MIDIPacketList>,
UnsafeMutablePointer<Void>,
UnsafeMutablePointer<Void>) -> Void =
{ (pktlist,readProcRefCon,srcConnRefCon) in
//Your code goes here...
}
let imp : COpaquePointer =
imp_implementationWithBlock(unsafeBitCast(block, AnyObject.self))
let callback : MIDIReadProc = unsafeBitCast(imp, MIDIReadProc.self)
Works with CoreFoundation callbacks.
Should work for CoreMIDI too.
Swift 2.x (New Way)
In Swift 2 the process becomes "less hacky" (and slightly more readable).
import CoreMIDI
let callback : #convention(c) (pktlist : UnsafePointer<MIDIPacketList>,
readProcRefCon : UnsafeMutablePointer<Void>,
srcConnRefCon : UnsafeMutablePointer<Void>) -> Void =
{ (pktlist, readProcRefCon, srcConRefCon) in
}
let usableCallback = unsafeBitCast(callback, MIDIReadProc.self)
I tried to declare a closure matching the following Objective-C block:
typedef void(^TyphoonDefinitionBlock)(TyphoonDefinition *definition);
like this:
var config: TyphoonDefinitionBlock = { (definition: TyphoonDefinition) in
definition.injectProperty("quest", with: nil)
}
. . . and got the following error. (see image).
What's the correct way to do this?
You need to declare definition as an ImplicitlyUnwrappedOptional (TyphoonDefinition!) because in objective-C it is a pointer that can be nil.
Normal variables (and constants) in swift cannot be nil. They must contain a value.
I'm using typealias, taken from http://berzniz.com/post/87924122326/notes-from-coding-in-swift
typealias resultBlock = (success: Bool, result: AnyObject!) -> Void
Like to explain it in details,start with your piece of code
Objective C
typedef void(^TyphoonDefinitionBlock)(TyphoonDefinition *definition);
In Swift you make it like this
typealias TyphoonDefinitionBlock = (definition:TyphoonDefinition?)->Void
If you want to intimate to caller object after particular moment you need to make a property.
var typhoonDefinitionCompletion:BlockTyphoonDefinitionBlock?
you can use typhoonDefinitionCompletionand you can raise the callback message like this.
self.typhoonDefinitionCompletion!(definition:passyourtyphoneDefinition)