I am creating a class that implements both the AVCaptureFileOutputDelegate and the AVCaptureVideoDataOutputSampleBufferDelegate protocols in Swift 2 (beta 3), but I am getting a compiler error due to the fact that both protocols define very similar functions (intentionally so; there are example Objective-C classes that implement both protocols).
The conflicting protocol extensions are AVCaptureFileOutputDelegate's captureOutput(_:didOutputSampleBuffer:fromConnection:) and AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_:didOutputSampleBuffer:fromConnection:). The only difference in the function signatures is the type of the captureOutput argument (AVCaptureFileOutput and AVCaptureOutput respectively).
I thought that using AVCaptureFileOutput in my function definition might work, since it is a subclass of AVCaptureOutput, but the compiler still reports the error:
Objective-C method 'captureOutput:didOutputSampleBuffer:fromConnection:' provided by method 'captureOutput(_:didOutputSampleBuffer:fromConnection:)' conflicts with optional requirement method 'captureOutput(_:didOutputSampleBuffer:fromConnection:)' in protocol 'AVCaptureVideoDataOutputSampleBufferDelegate'
Is there a way to work around this, or to force the compiler to use the function for both protocols? Any idea if this is just a Swift 2.0 issue that should be reported, or an area where Swift has a different approach than what is normally used in Objective-C?
Thanks.
I was not able to find a workaround, at least given the current Swift beta. I instead used a separate class as the AVCaptureFileOutputDelegate delegate. Not elegant, but functional.
Related
Headline: description called by super.init()
This is a new take on an old question. As a primarily Swift programmer I tend to not use NSObject for class definitions because of the residual side effects of Objective-C. Like if I have a read-only property called length and I then want to create a setter function called setLength, I get warnings about it conflicting with a similar definition from Objective-C. I just discovered the set(var){} setter. If I subclass a Cacoa class like UIDocument, etc. that inherit from NSObject, I have to live with these side effects.
I have a class that uses two other classes in the property definitions, none of them NSObjects. This class has a description computed variable that uses the description computed variables for the other two classes in its composition. All three classes need to conform to the CustomStringConvertable protocol. Ok, everything is good.
At some point this class got upgraded to being a UIDocument and the CustomStringConvertable became redundant and was removed. Everything still works.
Here is what I found out today. I wanted to break at a point in the program where it was printing one of the two properties and as a convenience I set the break point in the description variable for that class, thinking that it should only be called at the point I am interested in, where it is printed out. What I discovered is that the description variable gets called during all the super.init() of the UIDocument sub-class! And there were a few of them. I think composing strings as being relatively expensive but didn't care because they were only used in debug, but with them being called and who knows how they are used in super.init(), I need to change this.
I checked another UIDocument class in the same program that has 200 files associated with it and it is also calling description in super.init().
Does anyone have any input on the Best Practices for using description vs debugDescription?
I'm going to answer my own question as a matter of documentation.
I switched the UIDocuments subclasses to define and use debugDescription. I am debugging some code that loads all the files and does some manipulation and I was able to reduce the load time from 9.8 seconds to 6.8 seconds.
I also went through all the places where the Swift 3 conversion added String(describing:) to the program and found I could change a lot of them to using debugDescription and eliminate the String(describing:) wrapper.
I think the best practice is to only define and use debugDescription and for my non-NSObjects change conformance from CustomStringConvertable to CustomDebugStringConvertable.
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)
}
}
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
The documentation says
NOTE
Swift classes do not inherit from a universal base class. Classes you
define without specifying a superclass automatically become base
classes for you to build upon.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
It doesn't make much sense to me. There is a reason why Objective-C has a universal base class, and the same reason should apply to Swift, does it? NSObject manages retain/release semantics, a default implementation for isEqual:, hash and description. All this functionality is available in Swift too.
(Objective-C and Swift use the same runtime...)
So, what's up with that? Are Swift classes with no defined superclasses just NSObjects that pose as proper root classes under the hood? Or is the default object-behaviour duplicated for each new root-class? Or have they created another Swift-baseclass? The implementation of retain and release is really complex, because it needs to take multithreading and weak references into account at the same time.
Is there maybe a universal base class in Swift (despite what the documentation says)? It would be really handy, because in Objective-C I can e.g. write extensions that let me coalesce method invocations to the main runloop like [obj.eventually updateCounter] which can be read as "call -updateCounter the next time the main runloop gets in control. If, in the meantime, I call this method again, it should be called only once anyways. With this extension one could implement -[UIView setNeedsDisplay] as [self.eventually display]; This is no longer possible in Swift if there is no universal base class (or maybe it is, who knows?)
There are several object-oriented languages where one can define new root classes, including C++, PHP, and Objective-C, and they work fine, so this is definitely not a special thing.
There is a reason why Objective-C has a universal base class
As Sulthan mentioned, this is not true. There are multiple root classes in Objective-C, and you can define a new root class by simply not specifying a superclass. As Sulthan also mentioned, Cocoa itself has several root classes, NSObject, NSProxy, and Object (the root class of Protocol in ObjC 1.0).
The original Objective-C language was very flexible and someone could in theory come along and create his own root class and create his own framework that is completely different from Foundation, and uses methods completely different from retain, release, alloc, dealloc, etc., and could even implement a completely different way of memory management if he wanted. This flexibility is one of the things so amazing about the bare Objective-C language -- it simply provides a thin layer, all the other things like how objects are created and destroyed, memory management, etc., can all be determined by the user frameworks sitting on top.
However, with Apple's Objective-C 2.0 and modern runtime, more work needed to be done to make your own root class. And with the addition of ARC, in order to use your objects in ARC, you must implement Cocoa's memory management methods like retain and release. Also, to use your objects in Cocoa collections, your class must also implement things like isEqual: and hash.
So in modern Cocoa/Cocoa Touch development, objects generally must at least implement a basic set of methods, which are the methods in the NSObject protocol. All the root classes in Cocoa (NSObject, NSProxy) implement the NSObject protocol.
So, what's up with that? Are Swift classes with no defined
superclasses just NSObjects that pose as proper root classes under the
hood? Or is the default object-behaviour duplicated for each new
root-class? Or have they created another Swift-baseclass?
This is a good question, and you can find out by introspection with the Objective-C runtime. All objects in Swift are, in a sense, also Objective-C objects, in that they can be used with the Objective-C runtime just like objects from Objective-C. Some members of the class (the ones not marked #objc or dynamic) may not be visible to Objective-C, but otherwise all the introspection features of the Objective-C runtime work fully on objects of pure Swift classes. Classes defined in Swift look like any other class to the Objective-C runtime, except the name is mangled.
Using the Objective-C runtime, you can discover that for a class that is a root class in Swift, from the point of view of Objective-C, it actually has a superclass named SwiftObject. And this SwiftObject class implements the methods of the NSObject protocol like retain, release, isEqual:, respondsToSelector:, etc. (though it does not actually conform to the NSObject protocol). This is how you can use pure Swift objects with Cocoa APIs without problem.
From inside Swift itself, however, the compiler does not believe that a Swift root class implements these methods. So if you define a root class Foo, then if you try to call Foo().isKindOfClass(Foo.self), it will not compile it complaining that this method does not exist. But we can still use it with a trick -- recall that the compiler will let us call any Objective-C method (which the compiler has heard of) on a variable of type AnyObject, and the method lookup produces an implicitly-unwrapped optional function that succeeds or fails at runtime. So what we can do is cast to AnyObject, make sure to import Foundation or ObjectiveC (so the declaration is visible to the compiler), we can then call it, and it will work at runtime:
(Foo() as AnyObject).isKindOfClass(Foo.self)
So basically, from the Objective-C point of view, a Swift class either has an existing Objective-C class as root class (if it inherited from an Objective-C class), or has SwiftObject as root class.
This is mainly a design decision, there are languages which have a root class (e.g. Java) and languages which don't (e.g. C++).
Note that in Obj-C a root class is not enforced. You can easily create an object which doesn't inherit from any class. You can also create your own root classes, there are at least 3 in the Apple API (NSObject, NSProxy and deprecated Object).
The reason to have a root class is mostly historical - the root class ensures that all objects have some common interface, some common methods (e.g. isEqualTo:, hash() etc.) which are necessary for collection classes to work.
Once you have generics (or templates in C++), having a root class is not so important any more.
retain and release in NSObject are not important anymore since ARC. With MRC, you were still required to call them. With ARC you never call the methods explicitly and they can be implemented more efficiently behind the scenes.
In Swift, the methods from NSObject have been divided into protocols - Equatable, Hashable, Printable and DebugPrintable. That has the advantage that objects can share interfaces with structs.
However, there is nothing stopping you from inheriting every class from NSObject. The class is still there and it is especially useful if you are dealing with Obj-C APIs. In pure Swift, a root class is not necessary though.
One more note:
Swift classes doesn't run on top of Obj-C; they are not translated into Obj-C behind the scenes. They are just compiled by the same compiler which allows them to interoperate with each other. That's really important to understand. That's why #objc must be sometimes added to provide consistency with Obj-C protocols/classes.
Coming from a C++ background, one thing that confuses me about Objective C is the fact that you can add a method to a class without actually specifying it in the class interface. So I had a barrage of questions:
Why would someone choose to not add the method in the class interface?
Is it simply because of visibility?
Methods without a declaration in the interface are private?
Is declaring methods in a class interface just optional?
Is it different for overriding a base class' method?
The main difference is that C++ sets up much of its inheritance and types at compile time and Objective C does it mostly at runtime.
The only differences in putting a method in the interface (if all parameters are objects) in objective-C are that the compiler can see it at compile time and check that an object could respond to the method - if it does not then you get a warning but the compilation does succeed and the program will run and loo for the method at runtime. If the method is in the implementation of the class or a category (or some other way) then the run time will find it and call it successfully.
There are NO private methods you can call any method.
I believe that this is the only way to create private methods in Objective-C. The language does not support the ability to declare a private method so by not declaring a method in the header file you are making private from all callers.
Proper data encapsulation requires that you lock down access to members that either expose data or manipulates it. Not all members ought to be exposed.
Yes it is.
Yes, this is true.
Yes, this is true as well.
This I am not sure about - perhaps someone with more Objective-C knowledge could answer this one.
Extending Andrew Hare's answer to answer 5, no, it doesn't: whether declared in an #interface or otherwise, method replacement/refinement works the same.