In this article I wanted to delve into a nice generic abstraction in Swift. It resolves around method overloading, so we will start with a small introduction into general method overloading in Swift.
Method Overloading in Swift
There're multiple ways to overload or override methods in Swift. I don't want to go into the details here. If you're interested, this blog post has a pretty good overview. One of the possible overloading mechanisms is by type:
class Example method // "Foo"
Example .method // "{123}"
Example .
As you can see, you can call the same method name with different types,
and during compile time the correct code path will be determined and
optimized. So, unlike Objective-C
, there's no runtime dynamic
dispatch to figure out whether the argument to method
is a String or a
Int.
This is certainly nice, but what if your setup is more complex, and you don't know which kind of type you're getting in a generic function:
class
Now imagine that T can have different invariants. Let's say you have a protocol 'Storeable' for objects which can be stored in your database, and another protocol 'Interim for objects which should only exist temporarily in memory.
Generic Method Overloading by Protocol
You want to call 'store' on all your objects, without having to dynamically check the type to see if the object is Storeable or Interim. You can simply do that by adding the protocol:
class
Now, you can call the same method in your code, and the correct code path will be determined at compile time without any overhead.
Advanced Protocols
With this basic mechanism, you can do much more. as the Swift documentation points out, we can apply any kind of generic constraints in order to better structure our types.
You can overload a generic function or initializer by providing different constraints, requirements, or both on the type parameters in the generic parameter clause. When you call an overloaded generic function or initializer, the compiler uses these constraints to resolve which overloaded function or initializer to invoke. (Apple Documentation)
Here're some examples:
/// Call for all Storeable types which are also equatable
func
/// Call only for collections, whose objects conform to Storeable
func
/// Alternatively:
func Generator.Element: Storeable>
You can also extend your Structs and Classes from a different file, to add additional functionality based on a value or object which is local to that class
// Objects which are stored in the cloud
protocol CloudStorable
extension DBStore
All in all this gives you great flexibility without the overhead of dynamic dispatch.