Cannot satisfy protocols (with associatedtype) comformance - ios

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.

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?

How can assing generic (associated type require) protocol as delegate to any controller?

I have a protocol that have associatedtype named MessageType.
protocol MessageProtocol: class {
associatedtype MessageType
func sendMessage(_ with: MessageType)
}
Then implemented it in the controller
extension MainController: MessageProtocol{
typealias MessageType = String
func sendMessage(_ with: MessageType) {
// sending message
}
}
My purpose is using the protocol as delegate in other controller like below.
final class AnotherController {
weak var messagerDelegate: MessageProtocol?
...
}
But I get error of that
Protocol 'MessageProtocol' can only be used as a generic constraint
because it has Self or associated type requirements
Is there any way to handle this error?
I've reading Big Nerd Ranch blog post about this situation.
https://www.bignerdranch.com/blog/why-associated-type-requirements-become-generic-constraints/
I've learned about the situation but no idea about how it can be achived?
Thanks,
I handle the situation like that.
final class AnotherController<T: MessageProtocol> where T.MessageType == String {
weak var messagerDelegate: T?
...
}
And if I want to create anotherController instance programmatically, I created like that.
let instance = AnotherController<MainController>(frame: CGRect.zero)
instance.delegate = self
...
Because of MainController is comfortable with MessageProtocol from
extension MainController: MessageProtocol{}
Probably it's not common case but worked for me.

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)

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