How can I call NSStringFromProtocol with a Swift protocol? - swift

Assuming some
protocol MyCoolProtocol {
....
}
the following code refuses to compile (as of Swift 2.1):
let protocolName = NSStringFromProtocol(MyCoolProtocol)
because MyCoolProtocol is not of type Protocol. (This, at first glance, seems really weird, but if you dig enough it [unfortunately] makes sense)
How can I get the name of my Swift protocol in a String?

There are two ways:
The most common suggestion I can find is to declare your protocol as #objc. This seems odd when you have no intention of referring to this protocol from Objective-C code.
You can use let protocolName = String(MyCoolProtocol). As of the current version of Swift, this gives exactly what you'd expect ("MyCoolProtocol") and is still checked at compile-time.

This is how I accomplished it:
let protocolString = String("\(MyProtocol.self)")

Related

Swift 5: How to make a Set containing Class Types (for NSXPCInterface)

In Apple documentation of NSXPCInterface.setClasses(_:for:argumentIndex:ofReply:), for Swift, the first parameter is described as:
An NSSet containing Class objects —for example, [MyObject class].
Hmmm, it looks as though someone neglected to update this from Objective-C to Swift. For Swift I presume it should be something like
A Set containing Class Types
(Someone please correct my wording.) But how do you make a Set containing Class Types? The compiler will not allow me to declare a Set whose member types are not do not conform to Hashable. This makes sense because hashes are needed to maintain uniqueness among members. But Class Types in Swift do not seem to be hashable. Try this in any Swift file, or a playground…
class Foo {}
let mySet: Set<AnyHashable>
mySet.insert(Foo)
The compiler complains:
Argument type 'Foo.Type' does not conform to expected type 'Hashable'
Or, more broadly, does anyone know how to use NSXPCInterface.setClasses(_:for:argumentIndex:ofReply:) in Swift 5?
The classes you pass need to be #objc classes (and subclasses of NSObject). You'll put them into an NSSet, and then cast that to Set:
let classSet = NSSet(object: Foo.self) as! Set<AnyHashable>
This is almost certainly a bug in NSXPCInterface (it needs some kind of "refine for Swift" or possibly a shim), and I suggest opening a defect at bugreport.apple.com.
As of Swift v5.6 (perhaps earlier), you can create the set without casting:
let classSet = NSSet().adding(Foo.self)
This is because adding() is declared like this:
open func adding(_ anObject: Any) -> Set<AnyHashable>

What's the mechanics behind extension's methods overriding with '#objc' attribute?

Kind of nerd question. It's unclear to me, what exactly makes this code works:
class Shape { }
extension Shape {
#objc func redraw() {
print("from ext")
}
}
class Circle: Shape { }
class Line: Shape {
override func redraw() { // Compiler error: Declarations from extensions cannot be overridden yet
print("from subclass")
}
}
let line = Line()
let shape:Shape = line
let circle = Circle()
line.redraw() //from subclass
circle.redraw() //from ext
shape.redraw() //from subclass
If I omit #objc keyword in extension, the code won't compile - it's expected behaviour since methods in extension use static method dispatch -> cannot be overridden.
But why adding #objc makes it work? According to documentation and most articles, all is #objc doing is making things visible to Objective-c runtime. To change method dispatch type there is a special keyword - dynamic. But seems it is not necessary here!
Help me figure out, why is adding #objc (and omitting dynamic) makes such things possible.
Extensions,
as the name already says, are supposed to extend/add/include methods
to an existing implementation, making them one of the most beautiful
things about Objective-C, and now Swift, since you can add code to a
class or framework you do not own. Therefore, it makes sense that
you’re not supposed to “replace” code in extensions, conceptually
speaking.
That’s why the compiler complains when you try to do it.
Also Check out this answer.
however this seems to be a support issue too, as swift compiler simply throw this error:
overriding non-#objc declarations from extensions is not supported.
According to Apple,
Extensions can add new functionality to a type, but they cannot
override existing functionality.
But that is not the case, as we are overriding from the extension not vice versa,
which takes us back to the declaration of extension.
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.) Here.
Going back to the legacy topic swift compiler vs Objc compiler,
Dynamic dispatch vs. Static dispatch .
And there is no official documentation from apple on why this is not supported from the swift compiler or if they have any future plans to fix this or consider it an issue at all.
However, there’s no such thing as Swift dynamic dispatch; we only have
the Objective-C runtime’s dynamic dispatch. That means you can’t have
just dynamic and you must write #objc dynamic. So this is effectively
the same situation as before, just made explicit.
And here is a great article talking about this topic deeply.

Workaround to bridge a *generic* protocol to Objective-C?

Let's say that we have the following Objective-C API:
- (id)foo:(Protocol *)proto;
Which is imported into Swift as:
func foo(_ proto: Protocol) -> Any
Yep, it's one of those things that gives us a proxy object. These tend to be annoying to use in Swift, so let's say we want to make a wrapper around this thing to make it a bit friendlier. First we define a couple of Objective-C-compatible protocols:
#objc protocol Super {}
#objc protocol Sub: Super {}
Now, we define a function that takes a protocol conforming to Super and passes it along to foo(), and then we call it with Sub as the parameter to see if it works:
func bar<P: Super>(proto: P.Type) {
let proxy = foo(proto)
// do whatever with the proxy
}
bar(proto: Sub.self)
Well, this doesn't compile. The error message given is:
error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
Here's some stuff that does (mostly) compile:
func bar<P: Super>(proto: P.Type) {
// when called with 'Sub.self' as 'proto':
print(type(of: proto)) // Sub.Protocol
print(type(of: Sub.self)) // Sub.Protocol
print(proto == Sub.self) // true
let proxy1 = foo(Sub.self) // compiles, runs, works
let proxy2 = foo(proto) // error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
}
Okay, it's the same as Sub.self in almost every way except that I can't pass it to something requiring an Objective-C protocol. Hmm.
The problem is that, although the fact that Sub conforms to Super means that it must be an Objective-C protocol, the compiler isn't realizing this. Can we work around that and get it bridged manually? Well, let's take a look at Protocol's interface, to see if there's anything that we can...
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
#interface Protocol : NSObject
#end
Oh. Hrm.
Well, the fact that Protocol is a full-fledged NSObject subclass suggests that this is all probably the work of my bestest favoritest feature, the magical Swift<->Objective-C bridge that performs non-trivial conversions on things without it being obvious what is going on. Well, this gives me an idea, at least; I should be able to manually invoke the bridge by casting to AnyObject and hopefully get at the Protocol object that way maybe by as!ing it or something. Does it work?
print(Sub.self as AnyObject) // <Protocol: 0x012345678>
Well, that's promising. And when I try it on my generic parameter?
print(proto as AnyObject) // Terminated due to signal: SEGMENTATION FAULT (11)
Oh, come on.
I suspect this is probably a bug in the compiler, and I plan to test a few things to determine whether that's the case, but since the Swift sources take a geologic age to compile, I figured I'd post this here while I'm waiting. Anyone have any insight and/or workarounds on what is going on here?
I know it's not pretty and "Swifty", exactly, but getting the Objective-C Protocol as appropriate for passing into foo can be achieved using NSProtocolFromString as follows:
let proxy2 = foo(NSProtocolFromString(String(reflecting: proto))!)
where String(reflecting:) is a useful way to get the fully qualified type name suitable for resolving via NSProtocolFromString.
I'd say the crash you encountered is a bug.
Okay, after investigating this a bit more, I've determined that it is indeed a compiler bug, and have filed a report on it: SR-8129. What appears to be happening is that the Swift compiler falsely assumes that proto will always be a metatype of a concrete class type, so it performs the bridging by emitting a call to swift_getObjCClassFromMetadata, which crashes when it encounters the protocol metatype. When Sub.self is explicitly cast to AnyObject, the compiler emits Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject instead, which appears to dynamically determine the type of the object and bridge it accordingly.
With this in mind, the workaround becomes apparent: cast proto to Any first, to destroy the type information associated with the generic and force the compiler to emit Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject. And indeed, this appears to work:
func bar<P: Super>(proto: P.Type) {
foo(proto as Any as AnyObject as! Protocol)
}
Not the prettiest thing out there, but probably preferable to looking up the protocol via string manipulation.

How to use a protocol with optional class methods in an extension with generic in Swift?

I am trying using extension for an existing class with class method like:
#objc public protocol MyProtocol {
optional class func foo() -> Int
}
And I am using this protocol in an extension with generic like:
extension MyClass {
public func bar<T: MyProtocol>() {
...
let x: Int = T.self.foo!() // if I do not use "self" or "!" here, I will have a compiler error
...
}
This should work but when I build it, Xcode says "Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1". If I do not use "optional" in the protocol, I do not need to unwrap the foo() in the extension and everything works well even if I remove the "self". Can anyone tell me why and how to make the optional work properly?
Thanks in advance.
It looks like you've found a (fairly obscure) bug in the Swift compiler that's causing it to crash.
Here's a reproduction that's all you need in a single file to crash swiftc:
import Foundation
#objc protocol P { optional class func f() }
func f<T: P>(t: T) { T.self.f?() }
(you don't need to call f for it to crash)
You should probably file a radar since the compiler crashing is never expected behavior no matter what your code.
If you tried to do this without the optional, it'll work (and you can even ditch the self). My guess is the implementation of generics doesn't currently account for the possibility of optional class-level functions.
You can do it without generics like this:
func f(p: MyProtocol) {
(p as AnyObject).dynamicType.foo?()
}
(there may even be a better way but I can't spot it).
You need the AnyObject cast because if you try to call .dynamicType.foo?() on p directly you get "accessing members of protocol type value 'MyProtocol.Type' is unimplemented". I wouldn't be surprised if the crash of the generic version is related to this.
I'd also say that its worth asking yourself whether you really need a protocol with optional methods (especially class-level ones) and whether there's a way to do what you want entirely statically using generics (as you're already semi-doing).

Swift avspeechsynthesizer different languages

utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:#"en-au"];
This exact line of code is how you can change the languages in obj-c. But i was wondering if anyone, could tell me how so it is implemented in swift. in documentation this line of code is used...
init!(language language: String!) -> AVSpeechSynthesisVoice
But i can't understand where i would implement it :/
Where you see init(paramName: ParamType) in the Swift interface for a type named Type, you call it with the syntax Type(paramName: paramValue). This is right at the top of the chapter on Initialization in The Swift Programming Language, which I'd recommend reading before getting more than trivially into Cocoa development with Swift.
Also worth reading is the section on Initialization in Using Swift with Cocoa and Objective-C, which repeats the above and also gives you the general rule for how ObjC initializers and factory methods automatically map to Swift initializers: if you have a ObjC class named Foo with the initializer initWithBar: and/or the factory class method fooWithBar:, it maps to the Swift initializer init(bar:) and you call it with the syntax Foo(bar: someBarValue).
So:
utterance.voice = AVSpeechSynthesisVoice(language: "en-au") // g'day, mate
Note that this specific initializer is of the form init! — that means that the underlying ObjC code can return nil, and that Swift wraps the result of the initializer call in an Implicitly Unwrapped Optional. Since AVSpeechUtterance.voice can accept an optional (including one with a nil value), you're in the clear. But if that ever changes, or if you need to deal with APIs that explicitly require a non-nil voice, you should check that optional; e.g.:
if let voice = AVSpeechSynthesisVoice(language: "en-au") {
// do something with voice
} else {
// pick another one, maybe?
}