I have some methods in Objective-C.
I created a subclass from Objective-C superclass.
Now I want to override the Objective-C superclass method in my Swift class.
My code:
#objc class JobBusinessTableViewCell: AdItemTableViewCell {
override func commonInit() {
super.commonInit()
}
override func updateWithAd(ads: ResponseSimpleAd) {
super.updateWithAd(ads)
}
}
Here AdItemTableViewCell is an Objective-C superclass.
I want to override updateWithAd method in Swift.
How can I do this?
I found the answer .. But looks weird ..
Worked Code :
public final override func update(with ad : ResponseSimpleAd) {
super.update(with: ad)
}
I don't understand the method name change in swift .. But working as expected ...
Related
If a subclass implements an optional function, calling the function on the protocol works as expected. Namely it'll call the child's method even though it's not implemented in the parent.
When we introduce generics to the parent class, this behavior stops working.
Here's a playground showing the problem...
import UIKit
#objc
protocol MyProtocol {
#objc optional func foo()
}
// MARK: - Works
class Parent: MyProtocol { }
class Child: Parent {
func foo() { print("Child.foo()") }
}
(Child() as MyProtocol).foo?()
// ✅ Outputs `Child.foo()`
// MARK: - Broken
class BrokenParent<T>: MyProtocol { }
class BrokenChild: BrokenParent<String> {
func foo() { print("BrokenChild.foo()") }
}
(BrokenChild() as MyProtocol).foo?()
// ❌ Bug: does not output anything
Why does this happen, and what workarounds are there to fix it?
Xcode 11.4.1
Not sure why it happens, but I suspect that the #objc protocol causes the Objective-C runtime to be used to dispatch the event, and Swift has some behind the scenes code to make the simple case work (and omitted the more complicated inheritance+generic case). I could totally be wrong on this though, and would love to learn the real reason.
Here are two snippets that can be added to the same playground that show the workarounds available, namely (1) implementing the method in the parent class, or (2) using the #objc keyword on the method name.
// MARK: - Fixed 1
class FixedParent1<T>: MyProtocol {
open func foo() {}
}
class FixedChild1: FixedParent1<String> {
override func foo() { print("FixedChild1.foo()") }
}
(FixedChild1() as MyProtocol).foo?()
// ✅ Outputs `FixedChild1.foo()`
// MARK: - Fixed 2
class FixedParent2<T>: MyProtocol {}
class FixedChild2: FixedParent2<String> {
#objc func foo() { print("FixedChild2.foo()") }
}
(FixedChild2() as MyProtocol).foo?()
// ✅ Outputs `FixedChild2.foo()`
Just add '#objc' to your func:
#objc func foo() { print("BrokenChild.foo()") }
I have a protocol like so :
public protocol SubmitAgeDelegate : class {
func changeSubmitButtonBool()
}
The problem is I want to call it in a generics class.
open class GenericController<UICollectionViewCell,UICollectionReusableView> {
weak var submitAgeDelegate: SubmitAgeDelegate?
Within a UITapGestureRecognizer
func tapGestureDidRecognize(_ gesture: UITapGestureRecognizer) {
if let myAgeDelegate = self.submitAgeDelegate {
print("inside delegate") //Never gets inside
myAgeDelegate.changeSubmitButtonBool()
}
}
Not too sure why it never gets called? Similar ways have worked in regular classes withing IBAction functions.
In my other class :
open class MyCell: ActionCell, SubmitAgeDelegate {
weak var submitAgeDelegate: SubmitAgeDelegate?
public override init(frame: CGRect) {
super.init(frame: frame)
submitAgeDelegate = self
initialize()
}
// Delegate
public func changeSubmitButtonBool(){
print("called ")
}
You are never setting the submitAgeDelegate of the GenericController. Having a member of the same name in your MyCell class doesn't help.
You need to get a reference to your GenericController in order to set its delegate; there's no way around that. (This doesn't have anything to do with generics; it's the same for non-generic classes.) Since it looks like you're using it as a UICollectionViewCell, you might use the reference that you make in tableView:cellForRowAtIndexPath: or similar.
I'm trying to perform a protocol extension method in the background:
performSelectorInBackground(#selector(retrieveCategories()), withObject: nil)
However I get the below error message:
Argument of `#selector` does not refer to an initializer or method
Here is my protocol declaration:
#objc protocol DataRetrievalOperations {
optional func retrieveCategories()
...
}
And my extension:
extension DataRetrievalOperations {
func retrieveCategories() {
...
}
}
How can I achieve this?
Try this:
#selector(DataRetrievalOperations.retrieveCategories)
With omitting class (or protocol) name in #selector(...) notation, Swift assumes the enclosing class, which may be a ViewController, I guess.
One more issue:
It seems Swift cannot implement #objc protocol methods with default implementation in protocol extension.
(I think I have heard something about this, but I couldn't find any articles for now.)
You may need to implement it in your own class's extension or find another way.
extension CategoriesViewController {
func retrieveCategories() {
//...
}
}
I need to add that this will solve the first issue and #selector(retrieveCategories) will work.
You can't add an #Objc method in a Protocol Extension. You need to extend the Class which inherits NSObject and that Protocol and add the objc function there like so:
#objc protocol DataRetrievalOperations {
optional func retrieveCategories()
}
class aClass: NSObject, DataRetrievalOperations {
func method() {
performSelectorInBackground(#selector(retrieveCategories), withObject: nil)
}
}
extension aClass {
#objc func retrieveCategories(){
}
}
This will work.
this is a follow up question to this : Assign ViewController to Class or vice versa
So i have a ViewController called SwipeStepViewController, it subclasses from ORKActiveStepViewController. In the SwipeStep class i try to override the default ViewController with my custom SwipeStepViewController.
I tried to override the +stepViewControllerClass
method and return my Custom Viewcontroller inside the SwipeStep class:
import ResearchKit
class SwipeStep:ORKActiveStep{
override func stepViewControllerClass(){
return SwipeStepViewController.self
}
}
but this does not work at all.
I use researchkit, but i guess it is a general swift question.
I don't have any experience with ResearchKit, but after taking a look at the Objective-C code I believe your method should be:
override class func stepViewControllerClass() -> AnyClass {
return SwipeStepViewController.self
}
To explain why you're getting the errors:
Method does not override any method from its superclass.
and
'SwipeStepViewController.Type' is not convertible to '()'
take a look at the class method (indicated by the +) you're supposedly overriding:
+ (Class)stepViewControllerClass {
return [ORKFormStepViewController class];
}
Compare this with your method:
override func stepViewControllerClass(){
return SwipeStepViewController.self
}
which is neither a class method, nor returns a class and it's clear where the errors are coming from.
Quite late to the party, but I believe your function should be as follows:
class SwipeStep : ORKActiveStep {
static func stepViewControllerClass() -> SwipeStepViewController.Type {
return SwipeStepViewController.self
}
}
That function should return a Class. Take a look at the section in the Swift reference on Metatype Types:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html
Your function should be
override func stepViewControllerClass(){
return SwipeStepViewController.self
}
I’ve got a Swift class Parent that has a method doSomething() and I want to detect (in Parent) if a subclass has overridden doSomething(). How do I do that?
class Parent {
func doSomething() {}
func subclassOverridesDoSomething() -> Bool {
// what goes here?
return true
}
}
class Child: Parent {
override func doSomething() {}
}
I know I can do this with NSObject or even the Objective C runtime functions, but how can I do it with Swift classes?
You can use the Objective-C runtime provided you expose the function with dynamic:
class Parent {
func doSomething() {}
func subclassOverridesDoSomething(t:Parent.Type) -> Bool {
let originalMethod = class_getInstanceMethod(t, "doSomething")
return originalMethod != nil
}
}
class Child: Parent {
dynamic override func doSomething() {}
}
Parent().subclassOverridesDoSomething(Child.self)
If you omit dynamic, it won't work because Objective-C can't see the method.
Well Swift doesn't really offer methods to do that.
Also in my opinion, there is no disadvantage in using Objective-c methods like method_getImplementation or the NSObject. You will have to use the methods objective-c offers you to solve your problem.
For example:
let selector = Selector("viewWillAppear:")
let originalMethod = class_getInstanceMethod(YourClass, selector)