How to get Objective-C block input parameter in Swift Closure - objective-c

I need use a Objective-C SDK in my Swift project, the Objc demo is in below
[[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
NSLog(#"reslut = %#",resultDic);
}];
It pass a block to payOrder:orderString:: function, but when I call it in Swift, the auto complete help me generate these code
AlipaySDK.defaultService().payOrder(orderString, fromScheme: self.aliAppScheme, callback: { ([NSObject : AnyObject]!) -> Void in
println("Pay Success")
})
in Swift the closure input parameter has no name, in Objc it named resultDict, but in Swift I don't know how to get it pointer, Please help me, Thanks

In the objective C block, it takes an NSDictionary parameter. With Swift closures, the closure is already typed so you don't have to declare NSDictionary as the type and you really don't even need -> Void. Also the , callback: is extraneous in Swift as well because of trailing closures so your final product should be:
AlipaySDK.defaultService().payOrder(orderString, fromScheme: self.aliAppScheme) { resultDict in
println("Pay Success")
}

Related

Convert complex Objective-C macros to swift

I've used a library named LinqToObjectiveC in my project and now I want to use it in swift files. this library uses complex objective-C macros which are not accessible in swift. I want to know how can I convert for example this macro to swift:
#define LINQKey(__key) (^id(id item){return [item valueForKey:##__key];})
First let's explain what that macro is doing.
It takes an argument called __key, stringifies it (# and #) and returns a closure (block) that will take an object as an parameter and returns the result of item.valueForKey(key).
We can't convert stringification to Swift, however, that's something that shouldn't be used even in Obj-C. Why should we have LINQKey(myKey) when we can have LINQKey(#"myKey")?
As a simple function that returns a closure:
func LINQKey(key: String) -> (AnyObject! -> AnyObject!) {
return { (item: AnyObject!) in
return item.valueForKey(key)
}
}

Bridging from Objective C to Swift with PromiseKit

Using PromiseKit 2.0 with Swift 1.2, I'm trying to use a PMKPromise that was created in Objective C from Swift.
Objective C code:
#interface FooTest : NSObject
+ (PMKPromise *)promise;
#end
Swift code (I've tried a number of variations, none of which work. This one is closest to the example given at http://promisekit.org/PromiseKit-2.0-Released/):
FooTest.promise().then { (obj: AnyObject?) in
self.obj = obj
}
Compiler error: Cannot invoke 'then' with an argument list of type '((AnyObject?) -> _)'
This doesn't work either:
FooTest.promise().then { (obj: AnyObject?) -> AnyPromise in
return AnyPromise()
}
Similar error: "Cannot invoke 'then' with an argument list of type '((AnyObject?) -> AnyPromise)'"
There are two different promise classes in PromiseKit, one for Swift (Promise<T>) and one for ObjC (AnyPromise). The Swift ones are generic and Objective-C cannot see generic classes, so this is why there are two.
If Foo.promise() is meant to be used in both ObjC and Swift then you are doing the right thing. If however you only intend to use this promise in Swift then I suggest rewriting it as a Promise<T>.
To use an Objective-C AnyPromise (PMKPromise is a deprecated alias for AnyPromise: prefer AnyPromise) in Swift code you must splice it into a an existing chain.
someSwiftPromise().then { _ -> AnyPromise in
return someAnyPromise()
}.then { (obj: AnyObject?) -> Void in
//…
}
There should be a way to start from an AnyPromise, probably I will add this later today:
someAnyPromise().then { (obj: AnyObject?) -> Void in
//…
}
Expect a 2.1 update. [edit: 2.1 pushed with the above then added]

Problems trying to port an Objective C completion block into Swift

I'm using a third party library written in objective C with the following method:
- (void)manageServerResponse:(NSURLResponse*)response NSData:(NSData*)data andNSError:(NSError*)error onComplete:(void (^)(NSInteger kindOfError, NSDictionary*jsonResponse))onComplete;
when I port it to swift I do the following:
typealias ResponseCompletedBlock = (NSInteger, NSDictionary?) -> Void
...
let completedResponseMethod : ResponseCompletedBlock = {(kindOfError: NSInteger, jsonResponse: NSDictionary?) -> Void in
self.onComplete(kindOfError, jsonResponse: jsonResponse)}
let responseManager: ResponseManager = ResponseManager.sharedResponseManager() as! ResponseManager
responseManager.manageServerResponse(response,
NSData: data,
andNSError: error,
onComplete: completedResponseMethod)
I'm getting this error:
Cannot invoke 'manageServerResponse' with an argument list of type
'(NSURLResponse?, NSData: NSData?, andNSError: NSError?, onComplete:
ResponseCompletedBlock)'
and if I replace the last sentence for
responseManager.manageServerResponse(response,
NSData: data,
andNSError: error,
onComplete: nil)
everything works, so I assume that the problem is with the block structure, but I've tried to change everything and the error remains.
Can you help?
NSDictionary * is mapped to Swift as [NSObject : AnyObject]!,
therefore the type of the response block should be
typealias ResponseCompletedBlock = (Int, [NSObject : AnyObject]!) -> Void
and consequently
let completedResponseMethod = {(kindOfError: Int, jsonResponse: [NSObject : AnyObject]!) -> Void in
// ...
}
One method to figure out the correct Swift signature of functions is to use the autocompletion in Xcode:
You start typing
responseManager.manageServerResponse(
and Xcode suggests
Per Apple docs:
When you bridge from an NSDictionary object to a Swift dictionary, the resulting dictionary is of type [NSObject: AnyObject].
Also, unless the Objective-C code is annotated for nullability, the Swift dictionary is an implicitly-unwrapped optional. Therefore your typealias should look like this:
typealias ResponseCompletedBlock = (NSInteger, [NSObject : AnyObject]!) -> Void
I'd also recommend changing the NSInteger to Int.
Is the typealias and completedResponseMethod absolutely necessary for your program to function? If not, this would probably solve your problem:
responseManager.manageServerResponse(response, NSData: data, andNSError: error, onComplete: {
$0, $1 in
self.onComplete($0, jsonResponse: $1)
}
As your error suggests, the error was due to a type mismatch. If you use the default local parameters, you'll be able to work around that particular problem.

Using Swift CFunctionPointer to pass a callback to CoreMIDI API

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)

Swift : Define a closure compatible with Objective-C block

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)