Can't pass a reference to a protocol where self is specified - ios

I have a project using the NVActivityIndicatorView library and I am trying to extract some logic from two view controllers. Both view controllers conform to NVActivityIndicatorViewable whose definition is:
// From the libaray. I don't want to modify this.
public protocol NVActivityIndicatorViewable {}
public extension NVActivityIndicatorViewable where Self: UIViewController
{
func startAnimating( ... ) { ... }
func stopAnimating() { ... }
}
And as a result, I expected to be able to pass one of these view controllers in and use the startAnimation and stopAnimation methods on it.
func sharedLogic(sender: NVActivityIndicatorViewable)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
However, this fails with the compiler error 'NVActivityIndicatorViewable' requires that 'NVActivityIndicatorViewable' inherit from 'UIViewController'
Trying this with sender: UIViewController, this fails with the compile time error Value of 'UIViewController' has no member 'startAnimating' as I expected.
func sharedLogic(sender: UIViewController)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
I have found two potential solutions:
Create an empty subclass that specifies both these types: (This new type doesn't contain any logic)
class ActivityIndicatorViewController: UIViewController, NVActivityIndicatorViewable { }
Use an extension to specify all view controllers can be activity indicators: (This causes redundant conformance errors on many classes)
extension UIViewController: NVActivityIndicatorViewable { }
Can I accomplish this without creating a new type?
Environment settings:
Xcode version: 10.1
iOS Deployment Target: 9.0
Swift version: 3

What you want here is a composition type - a type that both inherits from UIViewController and conforms to NVActivityIndicatorViewable:
UIViewController & NVActivityIndicatorViewable
You can use this directly as the parameter type for your method:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable)
Or you can create a typealias for it (though I can't think of a shorter name):
typealias SomeShorterName = UIViewController & NVActivityIndicatorViewable
And then you can use SomeShorterName as the parameter type.

You set constraint on the protocol and extended the constrained one. So if you want those two functions, you need a UIViewContoller conformed to your protocol.
Move functions to the original protocol to get what you need.
public protocol NVActivityIndicatorViewable {
func startAnimating( ... )
func stopAnimating()
}
Update due to comment:
If you want to leave the original Protocol untouched, use composition type for the function:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable) {
sender.startAnimating()
sender.stopAnimating()
}

Related

What is the problem with my generic protocol in Swift?

Couldn't get my head around how protocol generic types work in Swift. Same solution in Java or Kotlin would work without problems.
protocol ResultProtocol {
associatedtype T: ResultTypeProtocol
var result: T? { get set }
}
protocol ResultTypeProtocol {
func didGet()
}
protocol InteractorProtocol: ResultProtocol where T == InteractorResultProtocol {
func someLogicRelatedToInteractor()
}
protocol InteractorResultProtocol: ResultTypeProtocol {
func interactorResult()
}
class Interactor: InteractorProtocol {
typealias T = InteractorResultProtocol
var result: InteractorResultProtocol?
func someLogicRelatedToInteractor() {}
}
I get 2 errors in my code.
First one is when I put where generic constraint on another protocol
Second error is that my Interactor class doesn't conform to the protocol. When I click fix it will add another 'typealias T = type' and want me specify T again.
I want to know if there is another way to achieve this in Swift or how to fix this problem.
Idea behind this is to extend my interactor classes with generic property result which is used as delegate for other layers. Interactor is used via his protocol and it's injected in all other classes.
As #vadian comment says: "The type of the typealias must be a concrete type, not a protocol".
But if you want this class to be used with different InteractorResultProtocol then you can use generic for Interactor class also. And define T later.
class Interactor<ResultProtocolType: InteractorResultProtocol>: InteractorProtocol {
typealias T = ResultProtocolType
var result: ResultProtocolType?
func someLogicRelatedToInteractor() {}
}
Usage:
struct MyInteractorResultProtocol: InteractorResultProtocol {
func interactorResult() {}
func didGet() {}
}
let interactor = Interactor<MyInteractorResultProtocol>()

Swift UI use View in a Protocol (Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements)

I want to use View in a Protocol.
protocol Test {
var view: View { get }
}
Protocol 'View' can only be used as a generic constraint because it
has Self or associated type requirements
I just want to do the same thing as with my ViewController. Any idea?
protocol Test {
var viewController: UIViewController { get }
}
If I use an associated type, I get the error in my other protocols.
protocol Test2: Test { 
//STUB
}
Any idea how to solve this problem? Thanks :)
SwiftUI.View is a protocol, and because it uses Self (for example, in its body property), you cannot directly declare a property type as a View.
You can define an associated type on Test and constrain that type to be a View:
protocol Test {
associatedtype T: View
var view: T { get }
}
You can't directly use the protocol unless you declare it as associated type, but you can use the type erased AnyView instead:
protocol Test {
var view: AnyView { get }
}
Creating an AnyView instance might add some noise in the code, however it's easy to create.
Extending Cristik's solution:
protocol ViewFactoryProtocol {
func makeView(parameter: SomeType) -> AnyView
}

How to handle not used functions from delegates in view controllers

I have a very general view that is created and used by multiple view controllers with 2 buttons, one of them sometimes is hidden depending on the needs.
This view delegates the tap of the two buttons.
protocol TheViewsDelegate: class {
func button1Tapped()
func button2Tapped()
}
Let's put that ViewControllerA creates this view and needs both buttons, this view controller will have to implement both delegate functions and do something inside it.
Now let's say that ViewControllerB creates the same view but just needs one of the buttons. This view controller will have to still implement button2Tapped() even though it will never be called and used.
Is there a way to handle this nicely? I imagine there's a nice solution where I don't need to implement this button2Tapped() if I don't need it.
I thought about making it optional by giving a default implementation but I don't like this solution, I like (and I think it's a good practice) the compiler giving me an error when a method it's not implement. Someone can jump into the project and not realising that he/she hasn't implement button2Tapped when needs to be implemented.
Note: This is a very simple example just to illustrate my question, but the question is more broad as in what to do when a function in a delegate is defined by controller that don't need to implement it.
I believe you want to use:
optional func
There are a couple of ways of declaring a protocol method as optional, one is using optional func which requires using #objc syntax, which a lot of programmers apparently don't like, and the other requires declaring an empty body in the extension of a protocol (which makes it optional by default).
protocol TheViewsDelegate: AnyObject {
func button1Tapped()
}
extension TheViewsDelegate {
func button2Tapped() {}
}
class SomeViewController: UIViewController, TheViewsDelegate {
func button1Tapped() {
// implement
}
}
By giving the protocol an empty body inside an extension of the protocol, that method is optional and does not need to be implemented by conforming objects.
For comparison, the alternative:
#objc protocol TheViewsDelegate: AnyObject {
func button1Tapped()
#objc optional func button2Tapped()
}
class SomeViewController: UIViewController, TheViewsDelegate {
func button1Tapped() {
// implement
}
}

How does one specific a protocol in a function parameter?

I'm writing a factory class that is trying to work with custom protocol defined functions. The compiler throws an error, because I don't know how to add a protocol definition to a function parameter.
Example:
protocol MyCustomFunctions {
func customFunction()
}
class MyVC: UIViewController, MyCustomFunctions {
func customFunction() {}
}
class Factory {
func createButton(specificVC: UIViewController) // need protocol here
{
specificVC.customFunction() // error thrown
}
}
How can one specific a protocol during a variable definition?
Or is there another way?
First of all ,convention says classes start with a Capital letter.
class MyVC: UIViewController, MyCustomFunctions {
func customFunction() {}
}
Then what you need is the correct type in the argument
class factory: NSObject {
func createButton(specificVC: MyVC) // you need a class that conforms to protocol here.
{
specificVC.customFunction() // no error anymore
}
}
You have another option. You can simply promise in the argument that you won't disclose the full type of the object ,you will only say it's an opaque object that conforms to protocol.
class factory: NSObject {
func createButton(specificVC: MyCustomFunctions) // you need a class that conforms to protocol here.
{
specificVC.customFunction() // no error anymore
}
}
BONUS:
The way you could have reasoned about this and find an answer is this>
Error is thrown when I call specificVC.customFunction()...Hmmm...so this object can only run this function if it is of type that actually HAS the function. So let's take a look at the argument type - UIViewController - ..UIViewController certainly doesn't have this function. It's the MyVC or the Protocol.
Type safety in Swift is very strict. Just "follow the type flow" and you will be good.

Check if optional protocol method is implemented in Swift?

I have a swift protocol:
#objc protocol SomeDelegate {
optional func myFunction()
}
I one of my classes I did:
weak var delegate: SomeDelegate?
Now I want to check if the delegate has myFunction implemented.
In objective-c I can do:
if ([delegate respondsToSelector:#selector(myFunction)]) {
...
}
But this is not available in Swift.
Edit: This is different from: What is the swift equivalent of respondsToSelector? I focus on class protocols not on classes.
How do I check if my delegate has an optional method implemented?
Per The Swift Programming Language:
You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.
So the intention is not that you check whether the method is implemented, it's that you attempt to call it regardless and get an optional back.
You can do
if delegate?.myFunction != nil {
}
I've found it successful to add an extension to the protocol that defines basic default implementation and then any class implementing the protocol need only override the functions of interest.
public protocol PresenterDelegate : class {
func presenterDidRefreshCompleteLayout(presenter: Presenter)
func presenterShouldDoSomething(presenter: Presenter) -> Bool
}
then extend
extension PresenterDelegate {
public func presenterDidRefreshCompleteLayout(presenter: Presenter) {}
public func presenterShouldDoSomething(presenter: Presenter) -> Bool {
return true
}
}
Now any class needing to conform to the PresenterDelegate protocol has all functions already implemented, so it's now optional to override it's functionality.
I normally implement it like this:
self.delegate?.myFunction?()
if the delegate methods returns a value:
var result = defaultValue
if let delegateResult = self.delegate?.myFunction?() else {
result = delegateResult
}
//do something with result
Declaration
#objc public protocol nameOfDelegate: class {
#objc optional func delegateMethod(_ varA: int, didSelect item: Item)
}
Implimetation
if let delegate = nameOfDelegate {
delegate.delegateMethod?(1, didDeselect: node)
}
I know this question is 5 years old, but I would like to share what I found. My solution works as of 2021, XCode 11+, Swift 5.
Say I wanted to figure out whether the function sign follows the GIDSignInDelegate protocol and also know what all the optional functions for GIDSignInDelegate are.
I have to look at the source code of the GIDSignIn module, and this is how.
Click on jump to definition on the main module that is imported. It will lead to a file like this:
Copy the entire line, import GoogleSignIn.GIDSignIn and paste it in the ViewController or whatever .swift file (doesn't really matter).
Within the swift file, right click on the GIDSignIn part of the import line GoogleSignIn.GIDSignIn and jump to definition. This will lead you to the actual module with all the available functions (the functions not marked optional may be stubs, which are required functions in the delegate protocol):
From this file, I can see that there is a sign function that is a stub of GIDSignInDelegate and an optional sign function that is implemented as a method overload.
I used this for GIDSignInDelegate, but you can use the same method to figure out whether any function follows any delegate protocol.

Resources