Swift class conforming to Objective-C protocol - ios

In Objective-C I have the the following protocol:
#protocol GCKDeviceScannerListener <NSObject>
#optional
- (void)deviceDidComeOnline:(GCKDevice *)device;
- (void)deviceDidGoOffline:(GCKDevice *)device;
- (void)deviceDidChange:(GCKDevice *)device;
#end
When trying to conform to this protocol in Swift Xcode 6.1 autocompletes it like this:
class ViewController: UIViewController, GCKDeviceScannerListener {
override func viewDidLoad() {
super.viewDidLoad()
var deviceScanner = GCKDeviceScanner();
deviceScanner.addListener(self);
deviceScanner.startScan();
println("scanning");
}
func deviceDidComeOnline(device: GCKDevice!) {
println("deviceDidComeOnline()");
}
func deviceDidGoOffline(device: GCKDevice!) {
println("deviceDidGoOffline()");
}
func deviceDidChange(device: GCKDevice!) {
println("deviceDidChange()");
}
}
The code compiles and seemingly runs ok on the simulator. However, none of the listener functions are ever triggered. Everything works 100% of the time when running the demo project from Google written in Objective-C only. Because of the last part I'm assuming that the there aren't any problems with the network or hardware or anything like that.
It could be that I have missed something important from https://developers.google.com/cast/docs/ios_sender, but I would like to know if the Swift code itself is correct according to the protocol. As the protocol only has optional functions it's hard to know if it's right.

I have no experience with this library, but I think you should keep the reference to GCKDeviceScanner.
Try:
class ViewController: UIViewController, GCKDeviceScannerListener {
var deviceScanner = GCKDeviceScanner()
override func viewDidLoad() {
super.viewDidLoad()
deviceScanner.addListener(self)
deviceScanner.startScan()
println("scanning")
}

Apple's documentation on Protocols is long and complex.
It's easiest to think of optional protocol methods like Optional closures, and you can use it with optional chaining.
#objc class Something {
var delegate: GCKDeviceScannerListener?
func someCallback() {
delegate?.deviceDidComeOnline?(device)
}
}

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

Cannot satisfy protocols (with associatedtype) comformance

I'm working with CleanSWift in my new project and I'm facing that it's too wordy. To automate some of the basic stuff, I wrote following tools (simplified):
// MARK: - Presenter
protocol Presenter {
associatedtype DisplayLogic
var viewController: DisplayLogic? { get set }
}
protocol PresentationLogic {
func show(_ error: Error)
}
extension PresentationLogic where Self: Presenter, Self.DisplayLogic: DefaultDisplayLogic {
func show(_ error: Error) {
}
}
// MARK: - Display logic
protocol DefaultDisplayLogic: class {
// func present(_ error: Error)
}
protocol TableViewDisplayLogic: DefaultDisplayLogic {
// func reloadTableView(with sections: [Section])
}
When I try to implement the code above, generics seems to be broken. I'm getting an error saying "Type 'MyPresenter' does not conform to protocol 'PresentationLogic'." However, everything seems fine to me.
// MARK: - Controller
protocol MyDisplayLogic: DefaultDisplayLogic {
}
class MyViewController: UIViewController, MyDisplayLogic {
}
// MARK: - Interactor
protocol MyBusinessLogic {
}
class MyInteractor: MyBusinessLogic {
var presenter: MyPresentationLogic?
func test() {
presenter?.show(TestError.unknown)
}
}
// MARK: - Presenter
protocol MyPresentationLogic: PresentationLogic {
}
class MyPresenter: Presenter, MyPresentationLogic {
weak var viewController: MyDisplayLogic? // ** Here I get the error. **
}
Any ideas? Thank you.
Currently, MyPresenter doesn't fulfill the requirements of the where clause of the PresentationLogic extension, so it can't use the default implementation of show(_:). Specifically, it doesn't pass the test for Self.DisplayLogic: DefaultDisplayLogic. Therefore, it doesn't conform to PresentationLogic, and so it also doesn't conform to MyPresentationLogic, which inherits from PresentationLogic.
But why not? I think it's caused by how Swift works: protocols can't conform to themselves. In MyPresenter, Self.DisplayLogic is MyDisplayLogic. Though it is a descendent protocol of DefaultDisplayLogic, it still seems to function as "a protocol trying to conform to itself", so it doesn't work.
To demonstrate this, you can replace weak var viewController: MyDisplayLogic? with weak var viewController: MyViewController, and the error will go away, since it's a concrete type that conforms to DefaultDisplayLogic. Also, if you change Self.DisplayLogic: DefaultDisplayLogic in the where clause to Self.DisplayLogic == MyDisplayLogic, the error will go away because you are requiring a specific type rather than a conformance.
In your case, a possible solution is to make MyPresenter a generic class. For example:
class MyPresenter<ConcreteDisplayLogic: DefaultDisplayLogic>: Presenter, MyPresentationLogic {
weak var viewController: ConcreteDisplayLogic?
}
That will allow you to use the same where clause constraints for your default implementation of show(_:), while providing a type-safe, generic implementation of MyPresenter.
There is a limitation to this approach: you can't change the type of the value of viewController for a single instance of MyPresenter.

How to call a protocol's default function declared in a protocol extension?

I'm trying to call a protocol's default function that was declared in an extension:
protocol Tester {
func printTest()
}
extension Tester {
func printTest() {
print("XXXXTestXXXX")
}
}
class TestController: UIViewController, Tester {
let testing = Tester()// error here
override func viewDidLoad() {
super.viewDidLoad()
testing.printTest()
}
}
The error ''Tester' cannot be constructed because it has no accessible initializers' keeps appearing when I try to create an instance of the protocol. Whats the best way to use default functions in protocols?
You have to call the implementer, in your case it's TestController so :
self.printTest() will work
protocol Proto {
// func testPrint() <- comment this out or remove it
}
extension Proto {
func testPrint() {
print("This extension called")
}
}
struct Bar: Proto {
func testPrint() {
print("Call from Structure")
(self as Foo).testPrint()
}
}
Bar().testPrint()
// Output: 'Call from Structure',
// 'This extension call'
Protocols cannot be instantiated - ever - lets think of them Contracts
Classes CAN supposed to confrom to 1..n protocols.
The fact that your protocol provides a default implementation, just enables a class that conforms to it to inherit this default functionality if it doesnt need to provide a custom implementation.
a very cheap way in your case would be:
let testing = self
since your TestController class already conforms to Tester.
(Note I create a retain cycle)
===
another way would be to not use testing but self.printTest()
(This might be what you want)
Id reread the apple swift book and check out protocols (what they are and how they work)

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
}

Error conforming to a self made protocol in Swift

I've created a protocol in one of my ViewControllers above the class declaration like so:
#class_protocol protocol CRAAddCredentialDelegate {
func didAddCredential()
}
class CRAAddCredentialTableViewController: UITableViewController {
....
}
However, when I try to conform to this protocol:
class CRAMainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, CRAAddCredentialDelegate {
....
}
I get an error:
What am I doing wrong?
Have you added the function didAddCredential() to your CRAMainViewController class?
By adding the , CRAAddCredentialDelegate to the list of protocols, you're indicating that your class will provide all of the variables and functions that protocol includes.
So you need to actually provide them.
class CRAAddCredentialTableViewController: UITableViewController {
func didAddCredential() {
// add code here
}
....
}
You should implement the required protocol. In your case func didAddCredential() declared in the protocol CRAAddCredentialDelegate is not implemented , so its giving error.

Resources