JSQMessages cannot override method Swift 3 - ios

For some reason I cannot override methods in Swift 3 using JSQMessages.
These methods are defined in the JSQMessagesCollectionViewDataSource
public func senderDisplayName() -> String!
public func senderId() -> String!
When I subclass JSQMessagesViewController I try to implement the methods as such:
override func senderId() -> String {
return User.Wozniak.rawValue
}
override public func senderDisplayName() -> String! {
return getName(.Wozniak)
}
However I get the error that it does not override any method from its super class. When I remove override it says it conflicts with an Obj-C selector.

I have implemented this functionality in swift 3 with following Properties
self.senderId = "my ID"
self.senderDisplayName = "Wozniac"

You might try this instead:
open override func senderId() -> String {
...
}
but I'm not sure if it will fix your issue completely.
According to SE-0117: Allow distinguishing between public access and public overridability, which introduces the open keyword, the rules for imported Objective-C code are (emphasis mine):
Objective-C classes and methods are always imported as open. This means that the synthesized header for an Objective-C class would pervasively replace public with open in its interface.
Of course, assuming JSQMessages is still implemented in pure Objective-C.

Related

Objective-C accessing Swift Package functions

Goal: To use Swift Package within Objective-C.
All I'm trying to do is to have a simple, rudimentary understanding of the correct syntax of access Swift vars & func() from ObjC.
This is the second part of questions about using Objective-C with a Swift package.
Here's my Swift Package file that I'm trying to access...
I'm concentrating on the Swift class access.
But I would also like to know how to access the struct.
import Foundation
public struct RicStruct {
public private(set) var text = "Hello, World!"
public init() {
}
public func sayHello() -> String {
"Hello Ric!"
}
}
public class RicClass: NSObject {
#objc public var msg = "Mother has a feeling, I might be too appealing."
#objc public let text = "Hello Everybody!"
public override init() {}
public func sayHello() {
print(text)
}
public func doSomething() {
print("Inside doSomething()")
}
}
This is the Objective-C parent attempting to access the Swift-Package vars & func:
Question: What's the proper syntax of accessing:
the Swift class & struct functions,
the Swift var/let?
You declared
public func sayHello() {
print(text)
}
That makes the method sayHello public across module, but it does not expose it to Objective-C. Instead:
#objc public func sayHello() {
print(text)
}
I didn't add the '#objc' qualifier to the Swift function. I had mistakenly thought that it's not necessary due to being a member of a NSObject class per class declaration.
Also, didn't need to add the .h ObjC <--> Swift bridge file.

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()
}
}

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

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
}

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.

Using Swift Protocol Inheritance

I am trying to create a delegate that uses traditional polymorphism to compensate for a device being bluetooth LE, bluetooth, etc and can't seem to get the syntax right for casting.
Here is my parent protocol and class:
#objc protocol DeviceDelegate
{
func didConnectToDevice(name:String)
func didFailToConnectToDevice(name:String)
func didDisconnectFromDevice(name:String)
func didWriteData(data:NSData)
func didReceiveData(data:NSData)
}
class Device: NSObject
{
var delegate: DeviceDelegate?
}
Now here is the child class and protocol simplified down:
protocol BluetoothDelegate : DeviceDelegate
{
func didFindService(name:String)
func didFindCharacteristic(name:String)
}
class BLE: Device
{
func someFunc()
{
let bluetoothDelegate = (delegate as? BluetoothDelegate)
bluetoothDelegate.didFindService(UUIDString)
}
}
It throws the following error on the first line of that function:
Cannot downcast from 'DeviceDelegate?' to non-#objc protocol type 'BluetoothDelegate'
This doesn't make sense to me since it should allow casting to a child like a usual object does.
If I put #objc in front of BluetoothDelegate I get the following error:
#objc protocol 'BluetoothDelegate' cannot refine non-#objc protocol 'DeviceDelegate'
Anybody have any ideas on this?
When I copy your code and paste it directly into a playground and add #objc in front of your BluetoothDelegate definition, I get a message on this line:
bluetoothDelegate.didFindService("asdf")
'BluetoothDelegate?' does not have a member named 'didFindService'
Because you have used as?, there is a chance that bluetoothDelegate is nil. You should be using optional chaining here. Replacing with the following line reports no errors in the playground, indicating that you may have done something else in your code that you're not showing us.
bluetoothDelegate?.didFindService("asdf")
Alternatively, you could use this:
if let bluetoothDelegate = delegate as? BluetoothDelegate {
bluetoothDelegate.didFindService(UUIDString)
}
The message you're seeing about DeviceDelegate not being an objc protocol indicates to me that you have written these in two different files and maybe forward-declared DeviceDelegate incorrectly.

Resources