#interface ThreadsViewController : UIViewController <NSKeyValueObserving>
{
}
Error:Cannot find protocol declaration for 'NSKeyValueObserving'.
I'm using Foundation framework. Why do I have this error?
NSKeyValueObserving is an informal protocol:
An informal protocol is a category on NSObject, which implicitly makes almost all objects adopters of the protocol. (...) Implementation of the methods in an informal protocol is optional. Before invoking a method, the calling object checks to see whether the target object implements it.
You can not adopt an informal protocol using the angle brackets syntax, that is for formal protocols. With informal protocols, you simply implement the protocol methods. Your attempt to conform to the protocol NSKeyValueObserving in the #interface block:
#interface ThreadsViewController : UIViewController <NSKeyValueObserving>
is the reason why the compiler is complaining.
From Protocols:
Being informal, protocols declared in categories don’t receive much language support. There’s no type checking at compile time nor a check at runtime to see whether an object conforms to the protocol.
You should check the documentation for NSKeyValueObserving. In that you can find at top that in which framework resides. Just confirm that you are including that framework in your project.
Are you sure FoundationFramework is added to your project? If not, add it.
Related
I want to write a PAT and I don't care about Obj-C interoperability. The #nonobjc attribute sounds perfect but its designed for variables and methods only.
Anything similar for hiding protocols from Obj-C?
You seem to be misunderstood what the #nonobjc attribute is for:
From the docs:
nonobjc
Apply this attribute to a method, property, subscript, or initializer
declaration to suppress an implicit objc attribute.
If you scroll further down the page, it tells you what will have an implicit objc attribute on them:
The compiler implicitly adds the objc attribute to subclasses of any class defined in Objective-C. However, the subclass must not be generic, and must not inherit from any generic classes. [...] The objc attribute is also implicitly added in the following cases:
The declaration is an override in a subclass, and the superclass’s declaration has the objc attribute.
The declaration satisfies a requirement from a protocol that has the objc attribute.
The declaration has the IBAction, IBSegueAction, IBOutlet, IBDesignable, IBInspectable, NSManaged, or GKInspectable
attribute.
This does not include protocols, so protocols are never implicitly exposed to Objective-C. This means that you don't need the nonobjc attribute on protocols to suppress implicit objcs on protocols. Protocols, by default, are not exposed to Objective-C, unless you mark them with #objc.
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've never worked with objective C but have a fair bit of experience with C++.
What exactly is the difference between a superclass and a protocol in objective C? I read that a protocol is essentially a pure virtual class, but is that it? Is a protocol simply a specific type of superclass?
A protocol is a contract a class is going to conform to. When a class conforms to a protocol it tells the compiler that it will implement all the methods and all the properties declared in the protocol.
In Objective-C the class additionally needs a superclass. In a lot of cases this is NSObject. The superclass implements already a lot of methods (like isEqual:). A protocol never implements any methods or defines any property.
A protocol tells which properties/operations a class must implement. A superclass implements them and you can add your own stuff on top.
A protocol defines a set of method definitions that a class or struct must implement, very much like a Java interface.
A superclass is the class from which a given class inherits its method definitions, the implementation for those methods, and the instance and class properties.
I know "id" type, but what does id<Litigating> mean ?
#protocol Litigating
-(int) sue:( id<Litigating> ) someone;
#end
Think of Objective-C protocols as Java, C#, etc. Interfaces on speed.
This is a variable of any class, conforming to the protocol Litigation (this is as far as traditional OOP goes without jumping hoops):
id<Litigation> someone;
This is a variable of the class Company (and subclasses), that also conforms to Litigation:
Company<Litigation>* someone;
This is a variable of class Company, that also conforms to both Litigation and NSCopying**:
Company<Litigation, NSCopying>* someone;
id<SomeProtocol>
implies that this object implements SomeProtocol. It must be implementing all the required methods belonging to SomeProtocol.
It means that the parameter is not only of type id but also conforms to the Litigating (formal) protocol, cf. The Objective-C Programming Language.
For example, these are valid and will compile without (all) protocol stubs
public class ViewController: UIViewController, SFSpeechRecognizerDelegate {
}
class BLEController: CBCentralManager, CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
}
}
Edit: Solved! Creating a class without inheriting from UIViewController or CBCentralManager still does comply with the delegate protocols, but it does not comply with NSObjectProtocol. It just seems to be that I'm attempting to use the frameworks in an unintended way.
Why does my code compile without fulfilling all the protocol requirements?
What you are seeing here are optional protocol requirements. They are here because Swift code needs to interact with Objective-C, and Objective-C has them.
All methods except centralManagerDidUpdateState declared in CBCentralManagerDelegate are optional, from here:
The only required method is centralManagerDidUpdateState(_:); the central manager calls this when its state updates, thereby indicating the availability of the central manager.
And SFSpeechRecognizerDelegate, only contains one optional method, from here:
Use this protocol's optional method to track those changes and provide an appropriate response.
why does the code not compile if you remove the superclasses then?
This is because both of those protocols in your question also inherit from NSObjectProtocol, so they actually also have the additional requirements of NSObjectProtocol. UIViewController and CBCentralManager both inherits from NSObject, so they satisfy the NSObjectProtocol requirements, but your Swift class without a superclass doesn't.
Protocols with optional requirements don't have to inherit from NSObjectProtocol though. It's just that most of them are that way in the framework. You can for example do:
#objc protocol Foo {
#objc optional func foo()
}
class FooClass : Foo {
// compiles fine!
}
It compiles without needing the protocol stubs because all the requirement is optional. Check within the declaration of SFSpeechRecognizerDelegate it has only one requirement for a method called availabilityDidChange and you can see that it's optional from the keyword given at the beginning of the function.
In the second case, the class you have created doesn't inherit from NSObject but the first one does because it's a sub-class of UIViewController which in-turn is a sub-class of NSObject.