I know that Objective-C categories are called extension in Swift.
But recently i bumped in to this :
extension MyClass : GMSMapViewDelegate
{
func mapView(mapView: GMSMapView, idleAtCameraPosition position: GMSCameraPosition)
{
print("idleAtCameraPosition")
self.getAddressFromLocation(mapView.camera.target)
}
}
This looked like they are implementing delegate functions.This seemed a very good and clean way of implementing delegate functions. So I personally tried it and this works but I think I may be wrong because categories i.e. extensions are not supposed to do this.They were used to add extra functionality to other classes with out subclassing.
So my question is can we use extensions for such purpose or not?
If we can do this then extensions are more than just categories.Because i don't think we could achieve this by categories in Objective-C.
Thanks
From the official Swift Language Reference:
Extensions in Swift can:
Add computed instance properties and computed type properties
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Make an existing type conform to a protocol
Yes, extensions are a valid method to make a class conform to a delegate protocol.
Not only are extensions valid for this purpose, but they allow for better code organization and follow good style practice in Swift.
Yes you can, and should, do that. Putting the methods that implement a protocol into a class extension is considered good style in Swift. It separates out groups of methods based on what they do. I would suggest making liberal use of extensions in your coding.
Yes, extensions are meant to be able to allow existing classes to conform to new protocols, in addition to just adding functions to a class.
Related
In Objective-C, one should always prefix category methods, e.g. if extending UIView with the method descendants, you'd add zzz_ and make it zzz_descendants to avoid naming conflicts. Is that necessary for a function in extension UIView { ... } in Swift?
It's not necessary in order to compile. However, it's a good idea to prefix extension methods to avoid possible conflicts later on, in case Apple should introduce a method by the same name. An even more important reason, in my opinion, is to make clear that the extension method is custom code and not part of the standard API.
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 am trying to write a registry in Swift that maps from API's (Protocols) to Implementations (Classes). I would like to be able to provide the registry an API and receive back an instance of the class that implements it. In Objective-C this was fairly trivial - just call NSStringFromProtocol on the protocol and then use that as a key for a dictionary containing the classes that implement them. In Swift, however, we do not have this introspective capability. When I try to do the same I am told that MyAPI.protocol does not have a member "mirrorType". My question to you is how, in Swift, without using #objc protocols, I can map from a protocol itself to the class that implements it. Thanks!
By now it's not possible without using #objc. The solution I've found in this case is using the protocol name (string) as a key for the dictionary for this implementations (In my case I'll always have only one instance per Protocol).
Using #objc will force you to have all your implementations returning AnyObject the equivalent (id) in objective-C (if your function does not return a native objective-C type).
Hope that helps.
I use this auto coding lib a lot through my projects.
https://github.com/nicklockwood/AutoCoding
I think it is very useful. So I am wondering how I can implement the same extension in Swift?
Thanks.
Additional question:
Is it possible to write AnyObject into a file? Or it has to inherit from NSObject and conform NSCoding protocol?
From looking at this project, the object does not have to conform to NSCoding. But only Objective-C properties are saved. So for a Swift object, it will only work with the properties that are visible to Objective-C (with #objc and/or dynamic, and the type of the property is one that can be bridged to an Objective-C type).
I wrote a little utility that simplifies NSCoding in Swift: https://github.com/mustafa/SwiftCoding
My question may seem strange, but i wanna to get this idea around Stackers' minds, in my work i just noticed that every protocol is conformed by a class all the time, i wondered about the possibility to make a simple object conforms to protocols too, and if not (and almost not ), why not ??
Appreciate your concern
There are two parts to protocol conformance:
the object must respond to the required messages in the protocol definition
the object must return YES when asked whether it conforms to the protocol
So, can we arrange for an instance of NSObject to satisfy those two conditions? Yes. In fact, there are at least two ways to do so. First, let's make all instances of NSObject conform to the protocol. What we do is to define the protocol methods in a category on NSObject, which solves the first part. Then we "swizzle" (which means using the class_replaceMethod() runtime function) -[NSObject conformsToProtocol:], returning YES for our interesting protocol.
Now let's imagine that you want to make a specific instance of NSObject conform to the protocol. This is slightly easier. First, create a subclass of NSObject that conforms to the protocol and implements the required methods; there's no need to override -conformsToProtocol: because the runtime library can see that this class conforms. Now, at runtime, take your NSObject instance and call object_setClass() to switch its class to your conforming subclass.
You can add protocols to classes at runtime, using the runtime function class_addProtocol(). However, doing so when the class does not implement all the methods of the protocol will likely lead to bad results.