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)
}
}
Related
I updated Xcode to 10.2 today and I got the following errors:
Method cannot be marked #objc because the type of the parameter 2
cannot be represented in Objective-C
Function types cannot be represented in Objective-C unless their parameters and returns can be
I don't understand why
It was perfectly fine in 10.1.
This is an example that I have been using for years without any issues.
How can I make this code to compile without errors?
#objc public func myFunction(inputString: String, handler:#escaping ((success: Bool, outPut: NSArray)) -> Void) {
// do stuff
}
Delete the phrase outPut:. It was always illegal; Swift 5 just tightens up at last.
So:
#objc public func myFunction(inputString: String, handler:#escaping (NSArray) -> Void) {
I have the following Swift method:
public func login(userLogin: String, userPassword: String, completion:#escaping (_ result: Result<Data, PZError>) -> ())
And I would like to call it from Objective-C but can't figure out the closure portion:
[apiController loginWithUserLogin:userLogin userPassword:password completion:???];
What would my completion code look like in Objective-C?
It is not possible to call such a method directly from Objective-C.
If you Result type is an enum with non-trivial "case" objects or a struct, it won't work, because Objective-C doesn't support value types.
Also Objective-C doesn't support generic types like Result<T>.
One way to fix would be to make a wrapper, i.e. write a second method in Swift that adapts the types:
func login(userLogin: String, userPassword: String,
completion: #escaping (_ resultData: Data?, _ resultError: NSError?) -> Void)
In this method you would call your original login, and in the internal completion callback unwrap your Result into Data and NSError (converting from PZError), and pass 'em the caller's completion block.
Could you briefly explain what #objc and dynamic mean in Swift 4 using Xcode 9.x?
With tries and errors and following articles in the stackoverflow, I have eventually achieved this snippet to work. But I would like to know a little bit about those magical keywords.
class SampleViewController: NSViewController {
#objc class Parameters : NSObject {
#objc dynamic var value1: Double = 0 // bound to Value of a NSTextfield with NumberFormatter
#objc dynamic var value2: Double = 0 // as "parameters.value1" for the Model Key Path
}
#objc dynamic var parameters = Parameters()
#objc dynamic var value3: Double { // in the similar way as "value3" for the Model Key Path
get {
return parameters.value1 + parameters.value2
}
}
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
switch key {
case "value3" :
return Set(["parameters.value1", "parameters.value2"])
default:
return super.keyPathsForValuesAffectingValue(forKey: key)
}
}
}
Having fun with Xcode and its disassembler, I have found some. Thanks to Mike Henderson's comment.
Firstly, adding a #objc modifier seems to have the compiler write its corresponding symbol name in a __OBJC segment of executables and/or library files, which will be then used by the Objective-C run-time system.
otool -o filename command shows us the contents of __OBJC segment.
Secondly, adding a dynamic modifier seems to have the compiler insert additional assembler codes to interact with the Objective-C run-time system. The additional code realizes that accessing dynamic properties will be done through objc_msgSend() and its related functions. Similarly, calling dynamic methods also will be done through objc_msgSend().
Now, in my understandings, the jargon dynamic dispatch implies use of objc_msgSend() while static dispatch does no use of it. In the latter case, both accessing variables and calling functions will be done without intervention of the Objective-C run-time system, which is in the similar, but not exactly same, way of C++ ABI.
Apparently, static one is faster than dynamic one. But static one is incapable of Objective-C's magical benefits, though. With the programming language Swift, we have opportunities to utilize both aspects by choosing either static or dynamic dispatch depending on the situation, by omitting or adding those magical keywords, respectively.
Thanks!
Further readings:
Objective-C Runtime
Using Swift with Cocoa and Objective-C (Swift 4.0.3)
#objc means you want your Swift code (class, method, property, etc.) to be visible from Objective-C.
dynamic means you want to use Objective-C dynamic dispatch.
Swift 3 - dynamic vs #objc
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")
}
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)