Reputation: 9173
I have a function with following syntax:
func myfunc<V1,V2>(to: (V1)->V2, from: (V2)->V1)
and as you see, I'm providing 2 closures to this function. These 2 closures are used for converting V1
to V2
and vice versa. Remember that I cannot provide these conversions as extension
to V1
or V2
. I like to improve function usability and I want to know what is more common and better approach for providing these 2 as a Swift user.
I have though of 3 ways, each with its own pros and cons.
1st approach: Use current syntax.
func myfunc<V1,V2>(to: (V1)->V2, from: (V2)->V1)
Pros: User provides 2 closure, so he can organize code better. In addition, he can provide them in-place.
Cons: Function call will become long and can only use trailing closure
for second closure.
2nd approach: Use a single closure for both and distinguish them through a paramether.
enum ConvertClosure {
case .to(Any)
case .from(Any)
}
func myfunc<V1,V2>(operation: (ConvertClosure)->Any)
Pros: Function call becomes simpler and we can use trailing closure
too.
Cons: Responsibility of 2 closures are now in a single one, so it becomes more complex. Cannot add generic type checking and need to use Any
for enum case argument and return type of function.
3rd approach: Use protocol rather than closure.
protocol ConvertOperation {
associatedtype InVal
associatedtype OutVal
func to(_ val: InVal) -> OutVal
func from(_ val: OutVal) -> InVal
}
func myfunc<V1,V2>(operation: ConvertOperation)
where ConvertOperation.InVal == V1, ConvertOperation.OutVal == V2
Pros: Function call becomes simpler. We have type checking from generics.
Cons: We need to conform to protocol which cannot be done in place. Approach is not using closure, so it may not be very Swifty.
Which method is more suitable for a Swift user? Can you suggest any better method?
Upvotes: 0
Views: 44
Reputation: 130201
This might be a bit opinion based but the general rule is:
Use the most specific type as possible. You want types to catch bugs for you during compilation.
Use protocols when you want to reuse the functionality. A protocol will require you to use a struct/class that will implement it, therefore making it harder to use. Therefore it's better when you already have an object that can conform to that protocol or when the functionality is reused, therefore you can create an object and use it multiple times.
To comment more on your cases:
You should not be using trailing closures if there is more than one closure parameter because that affects readability. However, this is not a bad solution and it's pretty common.
This is generally a bad solution because it uses Any
type which breaks rule 1 above. Always minimize usage of Any
. Most applications should not use it at all.
This is not a bad solution, however see rule 2. I will be optimal only in specific use cases.
Also consider NSObject
is basically the same as Any
. In general you should avoid its usage in Swift code because you are basically resigning on type checking.
Also note that you should not aim for code to be as short as possible. Always aim for readability.
We cannot probably give a more specific advise without knowing the exact use case.
Upvotes: 2