"It's fun to write and use extensions for types like 'String' and 'Int,' but when I do this, my pull requests don't get approved because they don't adhere to SOLID principles. I wish we could write extensions within a protocol, so we could use our extensions wherever we inherit our protocol
Welcome to the forum.
Realistically it's up to your team to impose any coding rules like that. I just wonder which one of the five SOLID principles is broken in this case?
Not sure I got you right, but you can do:
protocol P {
func foo()
}
extension P {
func foo() {
print("foo")
}
}
extension String: P {}
Although I'm sure it won't change anything materially IRT breaking vs not breaking those mentioned coding rules.
When we write any method inside an extension, that method becomes available for the entire type's usage. The Interface Segregation Principle advocates that a method should only be accessible to the classes that actually need it.
In the example you provided, unfortunately, the method written inside the extension extension String: P {}
becomes accessible for all variables of type String without inheriting the P protocol.
Extensions in Swift follow the same access control rules as any other type of declaration, so if you're finding that the extensions you write are too far-reaching, you might want to leverage some of those tools to help.
For example, an extension can be scoped by placing it in a module and not making it public
— then, only types in that module will be able to see and call methods defined in that extension.
Alternatively, if the concern your team has is that these extensions aren't appropriate because they add functionality that doesn't make sense to use on all String
s/Int
s/whatever, and therefore they should not be widely accessible, then a better solution may be to introduce a new domain-specific type which contains a String
/Int
/whatever, and offers a domain-specific interface on top of that. (e.g., in much the same way that a URL
type may be implemented in such a way that it contains a String
and offers tools atop that, instead of offering URL-type extensions on top of String
itself)
The access controller doesn't exactly meet my needs. I couldn't explain it well with my limited English, but I believe I can demonstrate what I want by writing it in code :)
It doesn't accept such usage, but what I envision is precisely something like this.
When I created a local framework for such limited usage and used it by importing, it fulfilled my requirement. However, creating a framework for just one method didn't turn out to be an ideal solution.
Then use functions:
protocol Test {
func stringToInt(_ string: String) -> Int
}
extension Test {
func stringToInt(_ string: String) -> Int {
Int(string) ?? 0
}
}
class MyClass: Test {
let str = "12"
init() {
stringToInt(str)
}
}
class OtherClass {
let str = "12"
init() {
stringToInt(str) // 🛑 Error
}
}
If access control is insufficient to scope the extension as you would like, you could encapsulate your domain-specific extensions in a wrapper struct:
extension String {
struct MyDomainExtensions {
var string: String
}
var myDomainExtensions: MyDomainExtensions {
MyDomainExtensions(string: self)
}
}
extension String.MyDomainExtensions {
func someMethod() -> Int {
string.isEmpty ? 0 : 1
}
}
"some string".myDomainExtensions.someMethod()
I think this is the kind of thing Itai was alluding to. You can structure it like I have, as a computed property available on String
, or however you like (the computed property just makes it easier to construct from a String
).
In any case, wrapper structs help you to keep your domain-specific logic separated, so it will be clear at the usage site when some other domain is reaching in to extensions that it shouldn't access. You can also add protocol conformances to the wrapper.
When the language's access control can't model the isolation disciple you want, that kind of clarity is the next best thing.
The most optimal solution I can think of right now seems to be this. Thanks
The method I want to write will be public, so it should not be private or file-private. However, even though the method is written as general, I still wanted to restrict it from applying to all variables of the same variables. I know I can achieve this by creating a protocol, but I wish there was a way to do it with extensions as well