We have some code where a class is nested in an extension. After opening the project in Xcode 9, we get the 'Nested class has unstable name' warning.
When using Xcode auto-fix, it generate an #objc with some (generated?) param.
For example:
struct Something {
}
extension Something {
#objc(_TtCV16MyProject9Something4Some) class Some: NSObject, NSCoding {
required init?(coder aDecoder: NSCoder) {
}
func encode(with aCoder: NSCoder) {
}
}
}
All this compiles. But what the heck is this _TtCV16MyProject9Something4Some?
It seems to be unique because if we use the same one in two places, it will not compile (some generic project level compiler error).
If I replace the _TtCV16MyProject9Something4Some with anything else (say #objc(Something)), the project compiles fine, but that does not shad any more light really.
One important question, is the param randomly generated or is it an actual link to a real bridging name?
But what the heck is this _TtCV16MyProject9Something4Some?
It's the (Swift 3) mangled name for Something.Some. And as you haven't specified a custom name by which to expose the class to Objective-C, it is also the name by which the Obj-C runtime sees the class (as Obj-C has no concept of nested classes).
Therefore it's also the name that is encoded into an NSCoding archive of an instance of that class (allowing the instance to be re-constructed on decoding; the Obj-C runtime will be queried for the class that goes by that name).
The reason why you're getting this error is because name mangling for nested classes changed between Swift 3 and 4 – and may well change again in the future (until Swift is ABI stable at least). Therefore to allow archives encoded with Swift 3 mangled names to be decoded in Swift 4 and above, the compiler is suggesting you expose the class to Obj-C with the Swift 3 mangled name.
If you don't need to provide this backwards compatibility for Swift 3, then you can go ahead and expose the nested class to Obj-C with your own stable name (to ensure future mangling changes don't affect archive decoding). In this case, I would recommend something like #objc(Something_Some).
I think your class' name might contain a character not supported by Objective-C. In those cases, you can use the #objc(name) attribute to provide an alternative name to use in Objective-C, and what you are seeing is an autogenerated unique name. This is also why you can change that with anything you like and it will compile just fine.
There's a section in Apple's docs explaining this, Configuring Swift Interfaces in Objective-C in https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID55
Related
I've going through a Ray Wenderlich tutorial for MVVM and in the view it has
#available(*, unavailable)
public required init?(coder: NSCoder) {
fatalError("init?(coder:) is not supported")
}
I understand providing the required init, but I cannot follow why there is #available(*, unavailable).
The *indicates that availability on all platforms - surely we had that already? Aren't we then marking it as unavailable for all platforms - in this case won't the fatalError never be executed?
I've looked at the Swift guide https://docs.swift.org/swift-book/ReferenceManual/Attributes.html and still don't understand this
From the link you have provided,
The unavailable argument indicates that the declaration isn’t available on the specified platform. This argument can’t be used when specifying Swift version availability.
So essentially you are saying that this initialiser is unavailable on all platforms, which makes sense, as this initialiser is not implemented.
By adding this attribute, you make Swift issue an error when you try to use the initialiser, hence preventing people from calling it accidentally:
It is sometimes handy to make a piece of code unavailable without actually deleting it. Two use cases come to mind:
• A subclass making methods of the superclass unavailable. •
Legacy code that we need to keep in the source to keep supporting old
versions or documentation.
In such cases we can use Swift's available declaration attribute to tell the compiler that code using the marked object or function should not compile.
For example, if you have to subclass NSObject into something that has a stored constant which needs to be passed at initialization, and don't want to allow consumers to call init because it doesn't make sense to set a default value for the constant, you can make init unavailable to its consumers:
class Dummy: NSObject {
let foo: String
init(foo: String) {
self.foo = foo
}
#available(*, unavailable)
override init() {
fatalError()
}
}
NOTE
Unfortunately, as of 2.2, this kind of availability declaration is not converted into an Objective-C attribute, so Objective-C consumers will still see the methods and classes as available.
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.
Overview:
I have a protocol P1 which provides a default implementation of one of the Objective-C optional functions.
When I provide a default implementation of the optional function there is a warning
Compiler Warning:
Non-'#objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '#objc' protocol 'UIAdaptivePresentationControllerDelegate'
Version:
Swift: 3
Xcode: 8 (public release)
Attempts made:
Tried adding #objc but doesn't help
Question:
How do I resolved this ?
Is there a work around ?
Code:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
While I think I can answer your question, it's not an answer you will like.
TL;DR: #objc functions may not currently be in protocol extensions. You could create a base class instead, though that's not an ideal solution.
Protocol Extensions and Objective-C
First, this question/answer (Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c) seems to suggest that because of the way protocol extensions are dispatched under the hood, methods declared in protocol extensions are not visible to the objc_msgSend() function, and therefore are not visible to Objective-C code. Since the method you are trying to define in your extension needs to be visible to Objective-C (so UIKit can use it), it yells at you for not including #objc, but once you do include it, it yells at you because #objc is not allowed in protocol extensions. This is probably because protocol extensions are not currently able to be visible to Objective-C.
We can also see that the error message once we add #objc states "#objc can only be used with members of classes, #objc protocols, and concrete extensions of classes." This is not a class; an extension to an #objc protocol is not the same as being in the protocol definition itself (i.e. in requirements), and the word "concrete" would suggest that a protocol extension does not count as a concrete class extension.
Workaround
Unfortunately, this pretty much completely prevents you from using protocol extensions when the default implementations must be visible to Objective-C frameworks. At first, I thought perhaps #objc was not allowed in your protocol extension because the Swift Compiler could not guarantee that conforming types would be classes (even though you have specifically specified UIViewController). So I put a class requirement on P1. This did not work.
Perhaps the only workaround is to simply use a base class instead of a protocol here, but this is obviously not completely ideal because a class may only have a single base class but conform to multiple protocols.
If you choose to go this route, please take this question (Swift 3 ObjC Optional Protocol Method Not Called in Subclass) into account. It appears that another current issue in Swift 3 is that subclasses do not automatically inherit the optional protocol requirement implementations of their superclass. The answer to that questions uses a special adaption of #objc to get around it.
Reporting the Issue
I think this is being discussed already among those working on the Swift open source projects, but you could be sure they are aware by either using Apple's Bug Reporter, which would likely eventually make its way to the Swift Core Team, or Swift's bug reporter. Either of these may find your bug too broad or already known, however. The Swift team may also consider what you are looking for to be a new language feature, in which case you should first check out the mailing lists.
Update
In December 2016, this issue was reported to the Swift community. The issue is still marked as open with a medium priority, but the following comment was added:
This is intended. There is no way to add the implementation of the method to every adopter, since the extension could be added after the conformance to the protocol. I suppose we could allow it if the extension is in the same module as the protocol, though.
Since your protocol is in the same module as your extension, however, you may be able to do this in a future version of Swift.
Update 2
In February 2017, this issue was officially closed as "Won't Do" by one of the Swift Core Team members with the following message:
This is intentional: protocol extensions cannot introduce #objc entry points due to limitations of the Objective-C runtime. If you want to add #objc entry points to NSObject, extend NSObject.
Extending NSObject or even UIViewController will not accomplish exactly what you want, but it unfortunately does not look like it will become possible.
In the (very) long-term future, we may be able to eliminate reliance on #objc methods entirely, but that time will likely not come anytime soon since Cocoa frameworks are not currently written in Swift (and cannot be until it has a stable ABI).
Update 3
As of Fall 2019, this is becoming less of a problem because more and more Apple frameworks are being written in Swift. For example, if you use SwiftUI instead of UIKit, you sidestep the problem entirely because #objc would never be necessary when referring to a SwiftUI method.
Apple frameworks written in Swift include:
SwiftUI
RealityKit
Combine
CryptoKit
One would expect this pattern to continue over time now that Swift is officially ABI and module stable as of Swift 5.0 and 5.1, respectively.
I just ran into this after enabling 'module stability' (turning on 'Build libraries for distribution') in a swift framework I use.
What I had was something like this:
class AwesomeClass: LessAwesomeClass {
...
}
extension AwesomeClass: GreatDelegate {
func niceDelegateFunc() {
}
}
The function in the extension had these errors:
'#objc' instance method in extension of subclass of 'LessAwesomeClass' requires iOS 13.0.0
Non-'#objc' method 'niceDelegateFunc' does not satisfy requirement of '#objc' protocol 'GreatDelegate'
Moving the functions into the class rather than in an extension resolved the issue.
Here's another workaround. I ran into this issue as well, and cannot switch from UIKit to SwiftUI yet. Moving the default implementations into a common base class was not an option for me either. My default implementations were quite extensive so I really did not want to have all that code duplicated. The workaround I ended up using was to use wrapper functions in the protocol, and then simply call those functions from each class. Not pretty, but may be better than the alternative, depending on the situation. Your code would then look something like this:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
}
}
I want to declare an Objective-C method with a custom Swift class as one of it's arguments, like this:
+ (void)doCalculationsWithACustomSwiftObject:(CustomSwiftClass *)swiftObject andADictionanary:(NSDictionary *)ordinaryDictionary;
The aim is to call this method from Swift, pass an instance of the custom class "CustomSwiftClass.swift" so that the Objective-C code can access its properties. Is it possible, and if so, how do I declare the "CustomSwiftClass" to make the compiler accept it?
A couple of thoughts:
To make Swift class accessible to your Objective-C code, you have to import the system generated header. As the documentation says:
When you import Swift code into Objective-C, you rely on an Xcode-generated header file to expose those files to Objective-C. This automatically generated file is an Objective-C header that declares the Swift interfaces in your target. It can be thought of as an umbrella header for your Swift code. The name of this header is your product module name followed by adding "-Swift.h". (You’ll learn more about the product module name later, in Naming Your Product Module.)
For example, your Objective-C file would import this generated header:
#import "MyApp-Swift.h"
For more information, see Importing Swift into Objective-C section of the Using Swift with Cocoa and Objective-C: Mix and Match
If you're not seeing the xxx-Swift.h file, double check your target settings:
(Note, I searched for "swift" in the settings to narrow down the results.)
As you'll see, the following are all set:
"Install Objective-C Compatibility Header" is set to "Yes";
The "Objective-C Generated Interface Header Name" is set (and matches what I imported into my Objective-C code);
The "Objective-C Bridging Header" is also specified.
Note, the Objective-C bridging header is ostensibly solely for the purpose of making the Objective-C classes available to the Swift code (and you're currently trying to accomplish the converse), but I notice that this affects what code is generated in the compatibility header, too. So make sure you have a bridging header, too.
Of course, make sure your Swift file is included in the list of compile sources:
And make sure the class is subclassed from NSObject:
import UIKit
class MyObject: NSObject {
var text: String?
var value: Int = 0
}
Note, some Swift types are not available to Objective-C code. For example, Objective-C cannot not use some optional types (e.g. an optional type such as Int? will not work, whereas an optional class type of String? will). So if you are seeing the class, but not some of the properties, make sure they're Objective-C compatible.
But I gather from your question that you're having more fundamental problems, that you're not finding the -Swift.h header at all. But once you solve that, this is a consideration for individual properties.
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.