Swift: Can not use NSImage .imageNamed - swift

I am seeing imageNamed deprecated (or removed) from available options When I do the following:
var statusImage:NSImage? = nil
self.statusImage = NSImage .ImageNamed....
I have tried the Swift document provided by Apple and other placed.This seemed very trivial but could not find the solution for imageNamed. Am I missing something?

Use init(named: String!) instead: call it like NSImage(named: "foo").
The compiler automatically remaps ObjC class methods that are named as convenience constructors to work as Swift initializers. If a class method follows the naming convention of a convenience constructor (e.g. +[SomeThing thingWithFoo: bar:]), Swift remaps it to an initializer (e.g. call SomeThing(foo: aFoo, bar: aBar)). This also goes for a few methods that Apple identified as working like a convenience constructor (as in the case of imageNamed:).
In most cases, if you finish typing the class-method-style call to a convenience constructor, the compiler will give you an error that tells you how it's been remapped:
error: 'imageNamed' is unavailable: use object construction 'NSImage(named:)'
More generally, you can look at the autogenerated module "header" for an API symbol in Xcode by cmd-clicking that symbol (e.g. NSImage) in the editor, or look in Xcode's documentation viewer or the online reference docs for that API, to find the Swift syntax for using it.

Related

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.

"Use of unresolved identifier" using the new Swift 2.2 syntax for selectors on implicit setter

Migrating my code to Swift 2.2, I have a property var activeTextField:UITextfield? and the selector I was using was "setActiveTextField:". This method does not exist explicitly in my swift code.
With the new syntax, #selector(setActiveTextField) doesn't work: Use of unresolved identifier
I know I could use Selector("setActiveTextField:") but I'd loose the benefit of the new swift selectors.
So, what's the new way of doing this?
The issue here is that you're working with a property, not a method. This has two problems:
The ObjC setter/getter method pair for a property is generated at run time.
The #selector expression requires a Swift function/method reference.
When Swift compiles your source, it doesn't know that the ObjC -(UITextField*)activeTextField and -(void)setActiveTextField:(UITextField*)field methods will exist, so it can't generate function references for them. Since it can't generate a function reference, it doesn't have something it can use for the #selector expression.
For now, there isn't a way to use #selector to access property getters/setters — using Selector(...) with a string constant is your only option.
(This is actually just a new face on a longstanding known issue... it's the same reason you also can't pass a property getter/setter to something like map or filter. I think I've seen something on http://bugs.swift.org about this, but I'm not finding it at the moment.)
In Swift 3, SE-0064 was accepted to solve this problem. Now, you would generate that setter like so:
#selector(setter: activeTextField)

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?
}

Swift : Why is the class method struck out

As long as a Swift class extends from NSObject we can pass it to the Objective-C runtime and ask it to introspect it for us.
We have three options:
class
classForCoder
classForKeyedArchiver
. . however class is struck out. (See image). Why is this?
That's because class is a keyword in Swift, therefore any valid method cannot be named class. In the same way you cannot create a method named for, while or other keyword.
I wasn't able to reproduce the strike-out with my methods, however, naming a method var (or other keyword) in obj-c makes it impossible to be called from Swift.
Edit
I was wrong. It's still possible to call class from Swift using
var clazz: AnyClass? = self.`class`()
However, then the compiler says:
'Class' is unavailable: use 'dynamicType' instead
So the answer by Edwin Vermeers is the correct one.
As you can see in the documentation, it's only available in Objective C and not in swift.
See: https://developer.apple.com/documentation/objectivec/nsobject/1571950-class
I think this is because the AnyObject gives you enough information (More than the NSObject)
for instance you can do NSStringFromClass(BaseObject) in swift instead of the NSStringFromClass([BaseObject class]) that you do in Objective C

How do I pass a Swift class to an Objective-C method needing a Class?

I'm looking to use the Objective-C library saxy to parse some XML into objects, however the method signature is:
+ (id)rootXPath:(NSString *)xpath toMany:(Class)toType nsURI:(NSString *)nsURI;
I'm having trouble working out how I'd pass the Class object type as Swift does not seem to work with classes in this way. As a workaround I will probably make the domain model + a wrapper in Objective-C, but would be good to know if there's a way to do it in pure Swift.
I've tried the following, but I get a EXC_BAD_INSTRUCTION:
OXmlElementMapper.rootXPath("/route", toMany: Route.self, nsURI: "")
and this which produces a syntax error:
OXmlElementMapper.rootXPath("/route", toMany: Route.Type, nsURI: "")
MyClass.self is the correct way to reference a class, but perhaps you're missing #objc on the class?
See docs regarding interop