Swift bridging method call bug - swift

Objective-C method
typedef void(^CompletionHandler)(NSError *error);
- (void)openWithCompletionHandler:(CompletionHandler)completionHandler authType:(AuthType)authType, ...;
I have tried to convert the object c code into swift code.
I tried :
test().openWithCompletionHandler({ (NSError) -> Void in
}, AuthType.Test)
But this is code "Extra argument in call" compile error.
What should I do?

Swift doesn't bridge Objective-C methods with variable arguments* (...), so the `openWithCompletionHandler(:authType:) method you've declared isn't showing up at all in Swift.
From the error message you're getting, I can tell that test() is returning an AnyObject, which Swift is happy to call any Objective-C method on. UIDocument has an openWithCompletionHandler() method that takes a single closure as its only argument, so Swift is complaining that you're giving it too many arguments for that method (even though it's not the one you want). Clear enough?
If possible, you'll need to refactor the Objective-C method to something that Swift can understand.
*Note that this isn't entirely true - the NSArray initWithObjects: method is bridged to a Swift initializer with a variadic parameter, but Apple appears to be doing something non-public to make that work - similar to how they're able to clarify whether arguments and return values should be optional or not.

Related

How to get UnsafeRawPointer on the swift object?

My app uses the native C++ lib, there is a method that takes as an argument void*
void foo(void * obj) { ... }
in swift I can call this method like this
func boo(obj: MyCustomObj) {
foo(&obj)
}
and looks like really I get a void pointer on the object, but if I try to get an UnsafeRawPointer on the object like this
func boo(obj: MyCustomObj) {
var pointer = &obj <---- Use of extraneous '&'
foo(pointer)
}
I got an error
Use of extraneous '&'
What is the problem here?
EDIT
I understood that using withUnsafe*** I can get the pointer to the data, but what to do if my method has 2 params, would it looks like this
withUnsafePointer(to: myObjFirst) {
pFirst in
withUnsafePointer(to: myObjSecond) {
pSecond in
foo(pFirst, pSecond)
}
}
The & syntax does not mean "the address of" or "pointer to" like in C. In Swift, it is an in-out expression.
These can be used to create implicit pointer conversions as a convenience, and that can seem like C's "pointer to" meaning, but it has very different rules and behaviors. For example, there is no promise that obj even has an address. It may be a tagged pointer. Passing it via an in-out expression may allocate memory and copy the value to make the call possible. Similarly, when passing a "pointer to an array," Swift will actually pass a pointer to a contiguous block of values (which may have been copied to make them contiguous) which is not the same as the actual Array struct.
It is not meaningful to say var pointer = &obj. There is no in-out reference there.
There is no general way to take long-lived pointers to objects in Swift without allocating your own memory (and this is rare). The memory model doesn't promise the kinds of lifetimes you'd need to make that sensible. If your code did compile the way you expect it to, the call to foo(pointer) would still be invalid because there's no promise that obj exists at that point and the pointer could be dangling. (There are no references to obj after the first line, so Swift can and often will destroy it, even though it's still "in scope.")
The foo(&obj) syntax is basically a shorthand for:
withUnsafePointer(to: obj) { foo($0) }
It exists to make it easier to call C functions, but it doesn't mean that Swift pointers are anything like C pointers.
For much more on Swift pointers, see Safely manage pointers in Swift from WWDC 2020.

Objective-C method not visible in Swift when using Swift Enum

I have a method that uses a forward declaration of a Swift enum. Whenever I do this my method isn't visible in other Swift classes. Yet if I do a forward declaration for a class it is visible in Swift. Why is it not visible for an enum and how can I get around this?
// Objective-C Header file
#class ViewController;
typedef NS_ENUM(NSInteger, MyEnumType);
#interface ObjcViewController : UIViewController
- (void)doThis: (enum MyEnumType)type;
- (void)grabTheClass: (ViewController *)mySwiftClass;
- (void)doSomethingElse;
#end
//Swift File
#objc enum MyEnumType: Int {
case one = 1
case two = 2
}
#objc class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let controller = ObjcViewController()
controller.doSomethingElse()
controller.grabTheClass(self)
controller.doThis(EnumType) //ERROR: This will not compile
}
}
The forward declaration of the enum creates an incomplete type. And an incomplete type has very little usefulness. You can form a pointer to it, or you can call a function with a pointer to it, but that's it.
Unless you complete the enum declaration with the concrete definition, you won't be able to properly consume it, neither in Objective-C, nor Swift.
And this is why the Swift code can't see the method, because at the time the Swift compiler processes the bridging header (and this happens before any Swift code is processed), the enum is not yet materialised, thus is excluded from the bridged interface due to being an incomplete type.
If you add a method that takes a pointer to the enum:
- (void)doThisWithPointer:(enum MyEnumType *)type;
then the method will be accessible from Swift, though it will be imported as
func doThis(withPointer type: OpaquePointer)
which doesn't make it much more helpful either.
But if forward declarations are incomplete types, why does the #class works? Its because in Objective-C objects are passed as pointers, and pointers to incomplete types are usable at call sites (as we just saw). Thus, the method is available to Swift.
More, the #class statement is part of the Objective-C additions to the C language, which means the compiler can give them special treatment, like importing the method with the expected signature, instead of the OpaquePointer one. This thanks to the dynamic nature of the OOP additions over the C language.
It looks like doThis is a function, but you are missing the parentheses in the call. The line controller.doThis should instead read controller.doThis()
See if that helps.
Somebody else claimed in another post that the Swift enum needed to be declared as public in order to be visible in Objective-C. I haven't verified that however.
Edit:
Okay, after some experimentation and googling, I think you have to declare your enum in Objective-C using the NSEnum macro, and that makes it available in both languages.
I don't think Objective-C can "see" Swift Enums when they're defined in Swift.

Swift 2: Why is it necessary to apply ‘as!’ after getting new object by ‘copy()’?

I’ve created new SKEmitterNode object using copy() method. After that i’ve tried to write emitter.position but Xcode said «Ambiguous reference to member ‘position’». But, when i use type conversion «as! SKEmitterNode» after the «copy()», everything is ok. Can you explain me, please, why am i need to use «as!» in this case? I can’t understand this because when i check value type of «emit» variable in debugger, i can see that it’s already have the type SKEmitterNode, even without using «as! SKEmitterNode» after «copy()».
class GameScene: SKScene, SKPhysicsContactDelegate {
let bangEmitter : SKEmitterNode = SKEmitterNode(fileNamed: "MyParticle")!
func makeBang(position: CGPoint) {
// this method causes an error in second line
// but, emit is already have type SKEmitterNode, as debugger says
var emit = bangEmitter.copy()
emit.position = position
// this works ok
var emit = bangEmitter.copy() as! SKEmitterNode
emit.position = position
}
}
Because copy() is a method defined by NSObject and is meant to be overriden by subclasses to provide their own implementation. NSObject itself doesn't support it and will throw an exception if you call copy() on it.
Since it's meant for subclassing, there's no way to tell what the class of the object that will be returned. In Objective-C, it returns an id; in Swift, this becomes AnyObject. Since you, the programmer, know what kind of object you are copying from, you can use as! SomeClass to tell the compiler what kind of object the copy is.
This also speaks to the difference between ObjectiveC and Swift. Objective-C is dynamic. In Objective-C, every time you send a message, the run time will check if the object responds to the message. This happens at run time. In Swift, you call a method and this happens at compile time. The compiler must know the object's type in order to call the right function / method.
This explains why you get emit as an SKEmitterNode in the debugger - this is run time. The compiler doesn't know that at compile time.
Using the as! is an indicator that a check may fail.
Swift 1.2 separates the notions of guaranteed conversion and forced conversion into two distinct operators. Guaranteed conversion is still performed with the as operator, but forced conversion now uses the as! operator. The ! is meant to indicate that the conversion may fail. This way, you know at a glance which conversions may cause the program to crash.
Reference: https://developer.apple.com/swift/blog/?id=23
Look up the definition of the function copy() and you'll see that it always returns Any, therefore you always need to cast it to the object that you're seeking.

Getting Xcode to drop "No XXX method found" warning when delegating

This could be me doing the design pattern wrong.
I'm implementing asynchronous delegation in an application that uses NSURLConnection. An object wraps the NSURLConnection and handles its delegated messages; that works fine. Now I'm defining my own delegates in the object that uses it (NSURLConnection messages ConnectionWrapper, ConnectionWrapper messages NeedsToUseConnection, you get the idea), and that works as well, however, Xcode emits this warning:
No '-request:finishedWithResult' method found
This is, presumably, because I'm declaring the delegate I'm calling like this:
id<NSObject> delegate;
...and Xcode is checking what NSObject declares in the Foundation framework. My custom delegate message is not there. I am properly insulating the call:
if([delegate respondsToSelector:#selector(request:finishedWithResult:)])
[delegate request:self finishedWithResult:ret];
Aside from turning the warning off -- I like to work with as many warnings on as possible -- is there a way to communicate (either syntactically or via a compiler directive) that I am aware this message is undeclared? Should I, instead, be using an interface design pattern for this á la Java? Using id<WillReceiveRequestMessages> or something?
Open to suggestion.
A better way of doing it would be to create your own delegate protocol:
#protocol MyControlDelegate <NSObject>
#optional
- (void)request:(MyControl *)request didFinishWithResult:(id)result;
#end
Then, you would declare your delegate like this:
id <MyControlDelegate> delegate;
The compiler will no longer complain when you write this:
if ([delegate respondsToSelector:#selector(request:didFinishWithResult:)])
[delegate request:self didFinishWithResult:result];
The <NSObject> syntax is important in the protocol definition because it tells the compiler to incorporate the NSObject protocol. This is how your protocol gets methods like respondsToSelector:. If you left that out, the compiler would start complaining about respondsToSelector: instead.
This is, presumably, because I'm declaring the delegate I'm calling
like this: ...and Xcode is checking what NSObject declares in the
Foundation framework.
That is incorrect. If that were the case then you would get a warning about the object "may not respond to" the method, or something like that. This is a completely separate problem.
This warning is due to the fact that the compiler must know the signature of a selector in order to call it. This is because, behind the scenes, the compiler translates a method call to either objc_msgSend or objc_msgSend_stret depending on whether the method returns a struct type or not. If it doesn't know the return type, it will guess that it is not a struct, and use the first function. However, this could be wrong.
The solution is to have the method declared anywhere at all. It doesn't even have to be declared in the right class. You can declare it in some dummy protocol that is never used. So long as it is declared somewhere, the compiler will know and will be able to correctly compile it.

Printing arguments sent to forwardInvocation: subclass of NSProxy

I want to intercept messages sent to a proxy object by just printing the selector and arguments. Even if the proxy does not implement them and does not have a target object. Please help. I have looked at several options and Apple docs but they assume that you already know the target object. I want to do this cleanly without memory issues.
#implementation MyProxy
-(void)forwardInvocation:(NSInvocation*)anInvocation
{
// at this point I would
//like to fetch the arguments and put on an array
NSMutableArray *myArgs = .....;
NSLog(#"Invoking selector %#", theSelector);
NSLog (myArgs); // this should print me the list of arguments to the method
}
#end
// e.g
MyProxy *proxy = [[MyProxy alloc] init];
[proxy SendMeAnyThing: #"hello"]; // this should print me arguments
or [proxy add: 12 to: 89 sub: 89]; // should print the arguments
Thanks thanks
The arguments are of just about any C type, including Objective-C types, that may be passed to the method. Thus, they can't be represented in an array.
Note that invoking a method that is not declared anywhere doesn't make any sense. Quite specifically, the compiler has no idea how to encode the arguments at the call site to make a valid C function call (a method call is really just a C function call to objc_msgSend() or a variant). So, to answer your "any method, even if it doesn't exist" question; no, you can't do that.
You can, however, effectively eliminate all compiler warnings. If you eliminate all compiler warnings -- all "method foo:bar: has can't be found" type warnings -- then that means the compiler does have enough information and you can do whatever the heck you want with a proxy.
Furthermore (as I read more into your question), there is no need to implement any method on the proxy. A proxy can quite happily forward-into-NSInvocation any method call. Written correctly, you can have a proxy stand in for any object without issue.
What you need to do is enumerate the arguments using the API of NSInvocation, sussing out the types and then decoding each argument accordingly and printing appropriately.
You can grab the number of arguments by invoking numberOfArguments on the method signature of the NSInvocation instance (the signature can be had via the methodSignature method).
Then, loop through the arguments and call getArgumentTypeAtIndex: on the method signature to get the type. You would then most likely write a switch() statement on the type encoding to then call getArgument:atIndex: and decode appropriately based on type.
Note that argument 0 is self and argument 1 is _cmd; the SEL of the method that was invoked.