Greetings, traveler!
Have you ever encountered challenges with protocol method names? Sometimes, they look unsuitable or duplicate the name of another method. In any case, using a method with a specific name can be difficult. I believe this scenario is quite rare. However, Swift provides a solution. You can mark your method with the underscore attribute @_implements(ProtocolName, Requirement)
.
Note
There is a warning in Swift documentation about underscore attributes:
Usage of these attributes outside of the Swift monorepo is STRONGLY DISCOURAGED.
The semantics of these attributes are subject to change and most likely need to go through the Swift evolution process before being stabilized.
All the stable attributes are described here.
Example
Consider such a situation. You have an app where users log in and create a session, which can be interrupted by logging in on another device. This will guarantee that users use only one device per session.
Let’s create a protocol for this purpose.
protocol SessionListener {
func startListening()
}
And implement this protocol in the Client
.
class Client: SessionListener {
func startListening() {
// start session listening
}
}
Now, we have another task. We need to listen to the Network connection. If the device loses it, we must change the app’s UI.
Note
By the way, if you want to read more about Internet connection listening, you can check out this article.
Let’s create a specific protocol for this case.
protocol NetworkListener {
func startListening()
}
As you can see, both of our protocols have methods with identical names. Implementing them will present some challenges.
class Client: NetworkListener, SessionListener {
func startListening() {
// start network listening
}
func startListening() { // ❌ Error
// start session listening
}
}
❌ Invalid redeclaration of ‘startListening()’
To fix this, we can mark our methods with the underscore attribute @_implements(ProtocolName, Requirement)
and change their names.
class Client: NetworkListener, SessionListener {
@_implements(NetworkListener, startListening())
func startNetworkListening() {
// start network listening
}
@_implements(SessionListener, startListening())
func startSessionListening() {
// start session listening
}
}
Now, we can use it without any difficulties.
let networkListener: NetworkListener = Client()
networkListener.startListening() // start network listening
Conclusion
It is a rare situation. Nevertheless, the solution looks elegant. However, if someone is faced with this situation, they may wonder whether the single responsibility principle in the project has been violated. Moreover, we should remember that this attribute is not stable. So, it is not preferable to use it in the production code.
If you enjoyed this article, please feel free to follow me on my social media: