Dynamic swift - Part 2: @dynamicMemberLookup/@dynamicCallable
Swift dynamicMemberLookup dynamicCallable Estimated reading time: 5 minutesIn this article, I would like to explore one more dynamic feature available in the Swift language - @dynamicMemmberLookup
.
This new attribute introduce new behavior in Swift - something more related to scripting languages, but with swift type-safety. This attribute makes it possible to execute a subscript of an object when someone accessing properties.
This feature was added in Swift 4.2.
Related articles:
- Dynamic swift - Part 1: KeyPath
- Dynamic swift - Part 2: @dynamicMemberLookup
- Dynamic swift - Part 3: Opposite to @dynamicCallable - static Callable
- Dynamic swift - Part 4: @dynamicReplacement
introduction
According to Swift doc - “Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime. The type must implement a subscript(dynamicMemberLookup:)
subscript.”
The purpose of this attribute - is a simplification of the code by adding some syntax sugar into it. We can create a wrapper around some data. This data may be one, that can’t be checked at compile-time (so, in theory not type-safe). If we apply this to type - requested property value also will be checked at runtime, this means, that we can request property that even not exist.
Access to this data will be achieved by using subscript(dynamicMemberLookup:)
. The parameter name must conform to ExpressibleByStringLiteral
or KeyPath
, and return type - any Type u like.
practical usage
@dynamicMemberLookup
To use this feature u should mark u’r type with attribute @dynamicMemberLookup and declare subscript with a parameter that conforms to ExpressibleByStringLiteral
or KeyPath
and return any type. An external name for the parameter should be dynamicMember.
U can also have a few overloaded subscripts in type.
Using KeyPath
, thing become even better - we can access to properties of instance variables by omitting the name of the selected variable:
read more about KeyPath
I use this possibility not very often because as for me, it reduces understandability and readability of code - when u omit in the message chain a name of a variable that u use, u need additional time to inspect the code.
Also, such an approach hides from us a well-known problem with type coupling - Message Chains.
A message chain occurs when a client requests another object, that object requests yet another one, and so on. These chains mean that the client is dependent on navigation along with the class structure. Any changes in these relationships require modifying the client. source.
@dynamicCallable
This attribute, as mention in proposal - natural extension to @dynamicMemberLookup
.
As result, we can mark a type (a class, structure, enumeration, or protocol) as being directly callable.
@dynamicCallable
is the natural extension of@dynamicMemberLookup
SE-0195, and serves the same purpose: to make it easier for Swift code to work alongside dynamic languages such as Python and JavaScript fom Paul Hudson’s article”
To make u’r type conforming to this attribute, u should declare dynamicallyCall(withArguments:)
method or/and dynamicallyCall(withKeywordArguments:)
method.
If you implement both
dynamicallyCall
methods,dynamicallyCall(withKeywordArguments:)
is called when the method call includes keyword arguments. In all other cases,dynamicallyCall(withArguments:)
is called.
dynamicallyCall(withArguments:)
require a variadicparameter, that will become an array. For example: 1,2,3
will be transformed into [1,2,3]
.
dynamicallyCall(withArguments:)
this method use instance of type as a function name and call it with named arguments with the specified type. For example, if we call object(key: value)
where value is of type Int
, inside the function we get args
equal to ["key": value]
.
I prefer a simple example, that allows quickly understand how things work. So here one:
@dynamicCallable
and @dynamicMemberLookup
may be used on one type:
For now, I can’t see where we can get “win” from this attribute while using just Swift, but definitely, for other developers, there is must be a big “win” here (for one who works with Python, JavaScript, Ruby, or some other domain-specific-language - definitely, thus they describe the purpose in the original proposal).
Again, Swift was created as a very type-safe language, but such dynamism can throw away this idea. The good point here is that all understood this, and as result, we may use @dynamicMemberLookup
within KeyPath
, which makes its usage a bit safer.
If u like an idea about using an object as a function, u may check Callable feature, which is a “static” feature.
Related articles:
- Dynamic swift - Part 1: KeyPath
- Dynamic swift - Part 2: @dynamicMemberLookup
- Dynamic swift - Part 3: Opposite to @dynamicCallable - static Callable
- Dynamic swift - Part 4: @dynamicReplacement
Resources
Share on: