Protocol inheritance issue - ios

I try to set up various protocols that work hand in hand. Unfortunately I cannot make them work the way I want. Looking at the following code, I think my goal is obvious: I want to require a class that conforms to a protocol X. If it conforms to protocol Y instead but protocol Y inherits from protocol X, it should be accepted as a conforming class too. Instead I receive the following compile error
Unable to infer associated type 'VC' for protocol 'ViewModelType'
Inferred type 'ExampleViewControllerType' (by matching requirement 'viewController') is invalid: does not conform to 'ViewType'
Current setup:
protocol ViewModelType: class {
associatedtype VC: ViewType
weak var viewController: VC! { get set }
}
class ExampleViewModel: ViewModelType {
weak var viewController: ExampleViewControllerType!
}
protocol ViewType: class { }
protocol ExampleViewControllerType: ViewType { }
class ExampleViewController: UIViewController, ExampleViewControllerType {
}

I can see what you are getting at with the 'transitive' protocols, however your error is caused by your associatedtype declaration of VC as seen in the error.
Unable to infer associated type 'VC' for protocol 'ViewModelType'
I think the compiler is having difficulty here maybe because its an innapropriate use of the associatedtype declaration.
An associatedtype can be thought of as a placeholder for an unknown type.
By defining VC as an associatedtype you are letting any class that inherits ViewModelType decide what type VC should be.
In ExampleViewModel class you do this by setting the type using typealias in the conforming class.
Your viewController can then be an ExampleViewControllerType without causing the 'inferred' error
protocol ViewModelType: class {
associatedtype VC
var viewController: VC! { get set }
}
class ExampleViewModel: ViewModelType {
typealias VC = ExampleViewControllerType
weak var viewController: ExampleViewControllerType!
}
protocol ViewType: class { }
protocol ExampleViewControllerType: ViewType { }
class ExampleViewController: UIViewController, ExampleViewControllerType {
}

No!! it can't be conforming class (If it conforms to protocol Y instead but protocol Y inherits from protocol X, it should be accepted as a conforming class too). A protocol can inherit one or more other protocols and can add further requirements on top of the requirements it inherits. The syntax for protocol inheritance is similar to the syntax for class inheritance. you cannot extend a protocol to conform to another protocol. only a class satisfy all of the requirements enforced by protocol. you could extend the protocol to provide default implementations.
extension Y {
// default implementations
}
for more Protocol Inheritance

Related

Protocol extensions where Self is generic

I have a BaseViewController that is defined this way:
open class BaseViewController<ViewModel: ViewModelType, View: BaseView>: UIViewController { ... }
I'm trying to write an extension for a protocol that has a default behaviour for all my BaseViewControllers. Something like this:
protocol MyProtocol {
func doStuff()
}
extension MyProtocol where Self: BaseViewController<BaseViewModel, BaseView> {
func doStuff() { ... }
}
However. If I only tells that the generic types are the BaseViewModel and BaseView it doesn't works for my subclasses, even though those are the most lower level classes used here.
How can I write my protocol extension in a way it will works for all my BaseViewController classes?

Problem with type constrained protocols defined in a separate module

I'm running into an issue where type constrained protocols defined in a separate module aren't acknowledging the constrained type.
For example if I define the following protocol:
public protocol SomeVCProtocol where Self: UIViewController {
func something()
}
And conform to it:
extension SomeViewController: SomeVCProtocol {
func something() { }
}
let someVC: SomeVCProtocol = SomeViewController()
someVC.something()
someVC.present(UIViewController(), animated: true)
There's no issue here. I can call the protocol-specific methods and UIViewController methods. However the moment I define SomeVCProtocol in a separate module, I no longer have access to the UIViewController methods. Is this just a shortcoming of Swift, or am I just missing something?

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.

Swift delegate does not inherit NSObject

I am trying to implement some sort of delegate broadcaster (Observer Pattern) in Swift to register multiple delegates. To use the "isEqual" function I need the generic to inherit from NSObject
To avoid duplicate code I prepared a generic DelegateBroadcaster:
import UIKit
class DelegateBroadcaster<T : NSObject>: NSObject {
var delegates : [T]
override init() {
delegates = []
}
func addDelegate(newDelegate : T) {
delegates.append(newDelegate)
}
func removeDelegate(oldDelegate : T) {
for i in 0...delegates.count-1 {
if (oldDelegate.isEqual(delegates[i])) {
delegates.removeAtIndex(i)
break
}
}
}
}
and subclass this for any specific broadcaster.
import UIKit
class NavigationControllerBroadcaster : DelegateBroadcaster<UINavigationControllerDelegate> {
}
But I get a strange error: "DelegateBroadcaster requires that 'UINavigationControllerDelegate' inherit from NSObject"
This is strange because the class reference by apple (Class Reference) says that UINavigationControllerDelegate inherits from NSObject.
So why do I get an error?
You are confusing class NSObject (NSObject class) and protocol NSObject (NSObject protocol, in Swift called NSObjectProtocol).
UINavigationControllerDelegate is a protocol and cannot inherit from class NSObject, it inherits from NSObjectProtocol (switch your documentation to Swift, you will see the difference).
UINavigationControllerDelegate is not a concrete type, it is a Protocol and therefore cannot be used as the type signature for the DelegateBroadcaster

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