Method with Objective-C selector conflicts - 'with' at the end - objective-c

After upgrading to Xcode 6.3.2.
I have two functions with (so I thought) different calls:
func someFunction(#str: String)
{
}
func someFunctionWith(#str: String)
{
}
The word 'With' seems to be added (?) to someFunction(# after the # sign.
The error I get is:
"Method 'someFunctionWith(str:)' with Objective-C selector 'someFunctionWithStr:' conflicts with method 'someFunction(str:)' with the same Objective-C selector"
The compiler is flagging this as selector conflicts, however, someFunctionWithStr: is not someFunction(str:), or is it?

#str will be translated to withStr in objc. You can use the #objc(someOtherSelectorName:) annotation to specify a different selector name for objc and avoid the conflict.

Related

What does #objc dynamic var mean in Swift 4

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

Typedef Return-Type in Objective-C does not work in Swift

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.

How to bridge throwable Swift initialiser with Objective-C code?

Let's say we have a Swift class with an initializer which can throw an error. This class must be used in Objective-C codebase (NSObject subclass):
import Foundation
enum EvenError : ErrorType {
case NonEvenNumber
}
class FooEven : NSObject {
var evenNumber : UInt
init(evenNumber: UInt) throws {
guard evenNumber % 2 == 0 else {
throw EvenError.NonEvenNumber
}
self.evenNumber = evenNumber
}
}
Produces compilation warning:
<unknown>:0: warning: no calls to throwing functions occur within 'try' expression
I can work around this warning in 2 ways:
by replacing throwable initialiser (init... -> throws) with failable one (init?)
giving up on subclassing from NSObject
Yet this way I will:
loose information about an error causing the exception,
have to make instances of FooEven optionals and / or handle many: if let fooEven = FooEven.init() {...} statements
... or I will not be able to use it in existing Objective-C code:
None of the above satisfies my needs / requirements.
Is there an other way to remove that warning without loosing information about the error?
Another workaround is to add a throwing convenience initializer that calls your non-throwing designated initializer.
This is a bug in the Swift compiler and is fixed in Xcode 8. When you upgrade Xcode, this warning will go away.
In the meantime, you can call super.init() at the end of your initialiser and this will also make the warning go away.

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]

How does work scheduleOnce with a selector and params?

Xcode is expecting a ')' just before the YES
[_creep scheduleOnce:#selector(removeFromParentAndCleanup:YES) delay:2.0f];
Sorry if it seems basic stuff... I just started ObjectiveC.
Because the Cocos API limits you to one selector with 1 argument (ccTime), write your own method that passes the given arguments to the proper function:
-(void)doneWithSomething {
[self scheduleOnce:#selector(removeAndCleanup:) delay:2.0f];
}
-(void)removeAndCleanup:(ccTime)delta {
[ _creep removeFromParentAndCleanup:YES];
}
You cannot pass arguments in the #selector() directive because it directly correlates to an entry in either a vTable (for common methods) or an entry in the ObjC sel cache, so the compiler thinks you're trying to invoke an impossibly named method called -removeFromParentAndCleanup:YES