Override of instance method form extension depends on deprecated inference of '#objc' - ios

I am trying to convert my code (written in Swift 3) to Swift 4, for that I am adding #objc where needed. Xcode has done quite a good job to automatically fix them but I am struggling with a few (all using the same 2 methods), where Xcode can't help, it just puts #objc somewhere in my code.
I am overriding a method called navbarRightButtonAction(button:) like this in my ViewController class.
class ViewController: PBViewController {
override func navbarRightButtonAction(button: PBAdaptiveButton) {
...
}
}
This is where I get the warning saying:
Override of instance method 'navbarRightButtonAction(button:)' from extension of PBViewController depends on deprecated inference of '#objc'
Then I thought the problem us be in the PBViewController class which looks like this:
extension PBViewController: PBNavigationBarDelegate {
func navbarRightButtonAction(button: PBAdaptiveButton) {
print("Override this method")
}
}
So I added #objc func navbarRightButtonAction(button: PBAdaptiveButton) but it didn't help.
Then I looked into the PBNavigationBarDelegate protocol
protocol PBNavigationBarDelegate {
func navbarRightButtonAction(button:PBAdaptiveButton)
}
I added #objc protocol PBNavigationBarDelegate but it didn't help either.
I have no other idea what to do to fix the deprecation warning.

Put #objc or #nonobjc in front of the extension:
#objc extension PBViewController: PBNavigationBarDelegate
Take a look at Limiting #objc Inference, SE-0160 at Swift Evolution for more details. It contains the following example regarding extensions:
Enabling/disabling #objc inference within an extension
There might be certain regions of code for which all of (or none of) the entry points should be exposed to Objective-C. Allow either #objc or #nonobjc to be specified on an extension. The #objc or #nonobjc will apply to any member of that extension that does not have its own #objc or #nonobjc annotation. For example:
class SwiftClass { }
#objc extension SwiftClass {
func foo() { } // implicitly #objc
func bar() -> (Int, Int) // error: tuple type (Int, Int) not
// expressible in #objc. add #nonobjc or move this method to fix the issue
}
#objcMembers
class MyClass : NSObject {
func wibble() { } // implicitly #objc
}
#nonobjc extension MyClass {
func wobble() { } // not #objc, despite #objcMembers
}

Related

Swift protocol extension in Objective-C class

I have a protocol written in Swift that should be conformed by several controllers and some of them are written in Objective-C. Not all of them need all methods from this Swift protocol so at first I decided to provide some methods with default implementation for making them 'optional' but in this case my Objective-C controllers don't recognize my Swift protocol. Did anyone face the same situation and did find a solution? Some sample of my code:
#objc public protocol SwiftProtocol: AnyObject {
func requiredMethod()
func optionalMethod()
}
extension SwiftProtocol {
func optionalMethod() {}
}
#interface ObjClass ()<SwiftProtocol>
And I've got the error : (59, 1) Cannot find protocol declaration for 'SomeProtocol'
Using #objc public in methods instead of extension gave the same result.
TIA for your help!
Objective-C protocols cannot have default implementations.
Objective-C protocols can have real optional methods/properties, unlike Swift protocols, which only have required methods/properties. The workaround for this in Swift is the use of a default implementation, however, sadly those cannot be seen in Objective-C.
I would suggest creating a pure Swift protocol and for all Objective-C classes that want to extend this, write the conformance in Swift, then create #objc wrapper functions in Swift that call the default protocol implementations - if it needs to be called, if it doesn't need to be called, simply ignore it.
Something along the lines of:
protocol SwiftProtocol {
func requiredFunc()
func optionalFunc()
}
extension SwiftProtocol {
func optionalFunc() {}
}
#objc extension ObjcClass: SwiftProtocol {
#objc func requiredFunc() {
print("do something")
}
// This will call the default implementation - can be omitted if you don't need to call the default implementation from Objective-C
#objc func objc_optionalFunc() {
optionalFunc()
}
}

Non-'#objc' method does not satisfy requirement of '#objc' protocol [duplicate]

This question already has answers here:
Non-'#objc' method does not satisfy optional requirement of '#objc' protocol
(3 answers)
Closed 1 year ago.
I've put the following into a Playground to try and understand this and I just don't:
import Foundation
#objc protocol Sample {
var value: Int { get set }
func increase()
func decrese()
}
extension Sample {
func increase() {
value += 1
}
func decrease() {
value -= 1
}
}
class Test: Sample {
var value: Int = 0
}
The error appears next to the class declaration for Test saying:
Non-'#objc' method 'increase()' does not satisfy requirement of '#objc' protocol 'Sample'
If I re-declare increase() and decrease() in the class then the warning is silenced. Or also if I remove the declarations from the protocol. Could someone please explain?
EDIT
I do need an Objective-C class to conform to this protocol as well, hence the #objc at the start.
The problem is that you’re defining these methods in a protocol extension. This is used to define a “default implementation” for a protocol (i.e. if a type doesn’t implement the method, the protocol’s implementation will be called).
But Objective-C doesn’t have the concept of default implementations for protocols. So it doesn’t makes sense to declare the protocol as #objc and have default implementations within the Swift protocol extension. An Objective-C class conforming to this protocol would never be able to enjoy these Swift default implementations.
The below code works with empty protocol methods' implementation in the Protocol extension class
import Foundation
protocol Sample {
var value: Int { get set }
func increase()
func decrease()
}
extension Sample {
func increase() { }
func decrease() { }
}
class Test: Sample {
var value: Int = 0
}
or if you want some default implementation of Sample protocol methods in the extension then use
import Foundation
protocol Sample {
var value: Int { get set }
func increase()
mutating func decrease()
}
extension Sample {
func increase() {
print("do anything")
}
mutating func decrease() {
value -= 1
}
}
class Test: Sample {
var value: Int = 0
}
mutating is added before the protocol method decrease() because it modifies the Protocol variable value.
If the Protocol extension doesn't modify any of the Protocol variable (e.g. increase()), then there is no need of mutating keyword

Declarations from extensions cannot be overridden yet in Swift 4

I have recently migrated my code to Swift 4. There is an issue that I am facing with extensions, i.e.:
Declarations from extensions cannot be overridden yet
I have already read multiple posts regrading this issue. But none of them entertains the scenario described below:
class BaseCell: UITableViewCell
{
//Some code here...
}
extension BaseCell
{
func isValid() -> String?
{
//Some code here...
}
}
class SampleCell: BaseCell
{
//Some code here...
override func isValid() -> String? //ERROR..!!!
{
//Some code here...
}
}
According to Apple,
Extensions can add new functionality to a type, but they cannot override existing functionality.
But in the above scenario, I am not overriding the method isValid() in extension. It is overridden in the SampleCell class definition itself. Still, it is giving the error.
But in the above scenario, I am not overriding the method isValid() in an extension.
isValid gets declared in an extension.
The error pretty much says that if a function is declared this way, it cannot be overridden.
The statement is valid for both from an extension and in an extension.
You can override declarations from extensions as long as you #objc the protocol. In Swift 4.2:
class BaseClass {}
class SubclassOfBaseClass: BaseClass {}
#objc protocol IsValidable {
func isValid() -> Bool
}
extension BaseClass: IsValidable {
func isValid() -> Bool { return false }
}
extension SubclassOfBaseClass {
override func isValid() -> Bool { return !super.isValid() }
}
BaseClass().isValid() // -> false
SubclassOfBaseClass().isValid() // -> true
In Swift 3, you were able to override the function of extension if extension was of a class that is getting derived from Objective-C (http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/), but I guess its not possible now in Swift 4. You can ofcourse do something like this:
protocol Validity {
func isValid() -> String?
}
class BaseCell: UITableViewCell, Validity {
}
extension Validity
{
func isValid() -> String? {
return "false"
}
}
class SampleCell: BaseCell {
func isValid() -> String? {
return "true"
}
}
let base = BaseCell()
base.isValid() // prints false
let sample = SampleCell()
sample.isValid() // prints true
I think this is self-explanatory.
declarations FROM extensions cannot be overridden yet
You are trying to override the function func isValid() -> String? which was declared within an extension of BaseCell, not the BaseCell class itself.
It is clearly saying that you can't override something that was declared inside an extension.
Hope it is helpful.
I too had a huge legacy of Swift 3 code that used this old trick to achieve what I wanted, so when I moved to Swift 4 and started getting these errors, I was somewhat distressed. Fear not, there is a solution.
This error has to do with the way Swift 4 compiles classes and the new way it treats Objective-C classes and functions. Under Swift 3, if a class is derived from NSObject, then all the variables and functions in that class would use Objective-C's dynamic naming and lookup conventions. This approach inhibited Swift's ability to optimise the code and improve code performance and size.
To overcome these penalties, in Swift 4, only variables and functions explicitly tagged with #objc get the Objective-C treatment, everything else uses standard Swift conventions: hence the error.
Armed with this knowledge, the solution to your problem is to tag the functions in the extension you wish to be overridden as #objc, then in the child classes, override the function, but remember to include the #objc tag so your code will get called at runtime.
WARNING The is a little gotcha here: if you forget to include the #objc in the override, the compiler will not complain, but your code lacks the dynamic lookup, so never gets called at runtime.
So your code should look a bit like this:
class BaseCell: UITableViewCell {
//Some code here...
}
extension BaseCell {
#objc func isValid() -> String? {
//Some code here...
}
}
class SampleCell: BaseCell {
//Some code here...
#objc override func isValid() -> String? {
//Some code here...
}
}
It is invalid in Swift, however not in Objective-C. So, if your method signature allows it (no objc forbidden constructs), you can declare it #objc func myMethod() and override it freely in Swift.

Class-Only Protocols in Swift

I want some of my classes (not all) to conform using 'Class-Only Protocols' from docs. What I am doing is
protocol RefreshData: class, ClassA, ClassB
{
func updateController()
}
and I am getting the errors
non class type 'RefreshData cannot inherit from classA
non class type 'RefreshData cannot inherit from classB
I'm not sure I am following exactly as in the docs. Does anyone have any ideas about this?
Swift 4 allows you to combine types, so you can have your protocol and then create, for example, a type alias to combine it with a specific class requirement.
For (a contrived) example:
typealias PresentableVC = UIViewController & Presentable
For the presented code:
The problem is that you're trying to limit to specific classes and Swift can't do that (at the moment anyway). You can only limit to classes and inherit from other protocols. Your syntax is for protocol inheritance but you're trying to use it as a class limitation.
Note that the purpose of class protocols is:
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
The answers provided by Chris and Wain are correct. I'm just adding a few more details here.
Defining a protocol
You must distinguish the concept of declaring a protocol (available for classes)
protocol RefreshData: class {
func updateController()
}
Defining a class
...from the concept of conforming your class to a protocol
class ClassA: RefreshData {
func updateController() {
}
}
Conforming a class you don't own
Sometimes you want to conform a class to a protocol but you don't own the source code for that class. In this case you can use an extension
extension ClassB: RefreshData {
func updateController() {
}
}
Latest version of Swift can do it!
I would do a protocol and protocol extensions that target the classes you want! (constraint the extension to specific class)
protocol Movable {
func moveForward()
func moveBackward()
}
extension Movable where Self: Car {
func moveForward() {
self.location.x += 10;
}
func moveBackward() {
self.location.x -= 10;
}
}
extension Movable where Self: Bike {
func moveForward() {
self.x += 1;
}
func moveBackward() {
self.x -= 1;
}
}
class Car: Movable {
var location: CGPoint
init(atLocation location: CGPoint) {
self.location = location
}
}
class Bike: Movable {
var x: Int
init(atX x: Int) {
self.x = x
}
}
protocol RefreshData : class
{
func updateController()
}
class ClassA : RefreshData
{
func updateController() {}
}
class ClassB : RefreshData
{
func updateController() {}
}

Swift class add "#objc" for why

Here's a link to a Swift tutorial.
I read the protocol section,i know if protocol is marked with the #objc:
#objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}
this mean this protocol in order to specify optional requirements and can be adopted only by classes
but tutorial didn't say why the class need to marked with the #objc too??
#objc class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement? {
count += amount
}
}
}
if i remove #objc from class , compiler didn't show error message too
so what different between add #objc to class or not?
Deprecation
It is no longer possible in latest Swift releases to use #objc w/o NSObject so this answer is deprecated.
Original
#objc is prefixed to classes to allow them to be used in ObjC. If you're dealing purely in Swift, it is unnecessary.
Also, if your class inherits from an ObjC class, the prefix is unnecessary.
For example #selector is owned by Obj-c,
In swift, we'll use #selector in .addTarget or in .target of an object. There we should prefix #objc for targeting method.
Example:
#objc func dummy(_ sender: UIButton) {
print("xxx")
}
above method will be using in a UIButton's target like
dummyButton.addTarget(self, action: #selector(self. dummy(_:)), for: .touchUpInside)
this is why we are using #objc as prefix in swift.

Resources