Swift
one ofSwift
‘s most powerful and most used features allows us to extend and develop newprotocol
andfunction
. Not only do we adjust the language andlibrary
to suit different projects, we also have the opportunity to write extensions that can be reused across a variety of use cases and projects.- In this article let’s study this issue as well as the principles to be able to memorize the overview of the
extension
so that it can be used in many different contexts.
1 / Generalizing through abstractions:
- For example, we will study the case when we need to improve performance, we write
function
that allow us to easily store articles on the hard drive:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">extension</span> <span class="token builtin">Article</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">cacheOnDisk</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> folderURLs <span class="token operator">=</span> <span class="token builtin">FileManager</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">.</span> <span class="token function">urls</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> <span class="token punctuation">:</span> <span class="token punctuation">.</span> cachesDirectory <span class="token punctuation">,</span> <span class="token keyword">in</span> <span class="token punctuation">:</span> <span class="token punctuation">.</span> userDomainMask <span class="token punctuation">)</span> <span class="token keyword">let</span> fileName <span class="token operator">=</span> <span class="token string">"Article- <span class="token interpolation"><span class="token delimiter variable">(</span> id <span class="token delimiter variable">)</span></span> .cache"</span> <span class="token keyword">let</span> fileURL <span class="token operator">=</span> folderURLs <span class="token punctuation">[</span> <span class="token number">0</span> <span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token function">appendingPathComponent</span> <span class="token punctuation">(</span> fileName <span class="token punctuation">)</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token function">JSONEncoder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">encode</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token keyword">try</span> data <span class="token punctuation">.</span> <span class="token function">write</span> <span class="token punctuation">(</span> to <span class="token punctuation">:</span> fileURL <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Expanding the specific
type
with the aboveextension
can be a good way to reduce code duplication while making it easier to perform normal tasks on source code. However, sometimes anextension
also makes the code less flexible and more detached. In this case, it is likely that we will not only storeArticle
but also othermodel
. - Let’s study to improve the reusable
extension
:Encode
each value inJSON
, we need anytype
that can be used in compliance with the libraryEncodable
protocol
.- We also need to match the
id
withtype
same type so that the unique file name can be calculated.
- To accomplish the above two requirements we use
Encodable
and add ageneric type constraint
to specify our cache method that can be called onIdentifiable
, providing us with theid
attribute we need:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">extension</span> <span class="token builtin">Encodable</span> <span class="token keyword">where</span> <span class="token keyword">Self</span> <span class="token punctuation">:</span> <span class="token builtin">Identifiable</span> <span class="token punctuation">{</span> <span class="token comment">// We also take this opportunity to parameterize our JSON</span> <span class="token comment">// encoder, to enable the users of our new API to pass in</span> <span class="token comment">// a custom encoder, and to make our method's dependencies</span> <span class="token comment">// more clear:</span> <span class="token keyword">func</span> <span class="token function">cacheOnDisk</span> <span class="token punctuation">(</span> using encoder <span class="token punctuation">:</span> <span class="token builtin">JSONEncoder</span> <span class="token operator">=</span> <span class="token punctuation">.</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> folderURLs <span class="token operator">=</span> <span class="token builtin">FileManager</span> <span class="token punctuation">.</span> <span class="token keyword">default</span> <span class="token punctuation">.</span> <span class="token function">urls</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> <span class="token punctuation">:</span> <span class="token punctuation">.</span> cachesDirectory <span class="token punctuation">,</span> <span class="token keyword">in</span> <span class="token punctuation">:</span> <span class="token punctuation">.</span> userDomainMask <span class="token punctuation">)</span> <span class="token comment">// Rather than hard-coding a specific type's name here,</span> <span class="token comment">// we instead dynamically resolve a description of the</span> <span class="token comment">// type that our method is currently being called on:</span> <span class="token keyword">let</span> typeName <span class="token operator">=</span> <span class="token function">String</span> <span class="token punctuation">(</span> describing <span class="token punctuation">:</span> <span class="token keyword">Self</span> <span class="token punctuation">.</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token keyword">let</span> fileName <span class="token operator">=</span> <span class="token string">" <span class="token interpolation"><span class="token delimiter variable">(</span> typeName <span class="token delimiter variable">)</span></span> - <span class="token interpolation"><span class="token delimiter variable">(</span> id <span class="token delimiter variable">)</span></span> .cache"</span> <span class="token keyword">let</span> fileURL <span class="token operator">=</span> folderURLs <span class="token punctuation">[</span> <span class="token number">0</span> <span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token function">appendingPathComponent</span> <span class="token punctuation">(</span> fileName <span class="token punctuation">)</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> encoder <span class="token punctuation">.</span> <span class="token function">encode</span> <span class="token punctuation">(</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token keyword">try</span> data <span class="token punctuation">.</span> <span class="token function">write</span> <span class="token punctuation">(</span> to <span class="token punctuation">:</span> fileURL <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
2 / Define the correct protocol:
- We rewrite the
Article
extension
, this time we use theResult
type to allow theresult instance
to carry anArticle
array so that it can becombined
with aninstance
same type:
1 2 3 4 5 6 | <span class="token keyword">extension</span> <span class="token builtin">Result</span> <span class="token keyword">where</span> <span class="token builtin">Success</span> <span class="token operator">==</span> <span class="token punctuation">[</span> <span class="token builtin">Article</span> <span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">combine</span> <span class="token punctuation">(</span> with other <span class="token punctuation">:</span> <span class="token keyword">Self</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">.</span> <span class="token function">success</span> <span class="token punctuation">(</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">+</span> other <span class="token punctuation">.</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- As with the
caching method
above, there is no requirement from the above code to know aboutArticle
. In this case, we need tocombine
2 collectionRangeReplaceableCollection
into one withRangeReplaceableCollection
.
1 2 3 4 5 6 | <span class="token keyword">extension</span> <span class="token builtin">Result</span> <span class="token keyword">where</span> <span class="token builtin">Success</span> <span class="token punctuation">:</span> <span class="token builtin">RangeReplaceableCollection</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">combine</span> <span class="token punctuation">(</span> with other <span class="token punctuation">:</span> <span class="token keyword">Self</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">.</span> <span class="token function">success</span> <span class="token punctuation">(</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">+</span> other <span class="token punctuation">.</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
3 / Avoid conflic and type polutiton:
- In case we build a storage place for frameworks, we can allow different projects to save and account values using
Container
. We define theDataConvertible
protocol so that we can create several system types as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span> <span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span> <span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span> <span class="token punctuation">(</span> utf8 <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span> <span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">pngData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> value <span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> value <span class="token punctuation">.</span> data <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- The above approach can work well with an independent project, but if we are going to share this functionality with multiple projects, then perhaps these problems become cumbersome and cumbersome.
- Because we have defined required
property
asdata
, there may be times when it will overlap with other definitions. The same is true for protocols that have generic names likeDataConvertible
. While the module name can useModuleName.TypeName
, attribute name cannot be used this way. - A good solution to this type of problem is to eliminate the
protocol
and addtype-specific
to theContainer
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> string <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token function">Data</span> <span class="token punctuation">(</span> string <span class="token punctuation">.</span> utf8 <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> image <span class="token punctuation">:</span> <span class="token builtin">UIImage</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> image <span class="token punctuation">.</span> <span class="token function">pngData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Error</span> <span class="token punctuation">.</span> failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token function">write</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- The above approach works well with a variety of
type
– what we want. The difficulty occurs when we want to add more configuration options and parameters to theAPI container
. - For example we want to be licensed for the
API
of theuser
to specify the level of sustainability of use when usingvalue
somehow.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> data <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span> permanent <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Tag</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> string <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span> permanent <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Tag</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">Data</span> <span class="token punctuation">(</span> string <span class="token punctuation">.</span> utf8 <span class="token punctuation">)</span> <span class="token keyword">try</span> <span class="token function">write</span> <span class="token punctuation">(</span> data <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> persistence <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> tags <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> image <span class="token punctuation">:</span> <span class="token builtin">UIImage</span> <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span> permanent <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Tag</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> image <span class="token punctuation">.</span> <span class="token function">pngData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Error</span> <span class="token punctuation">.</span> failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token function">write</span> <span class="token punctuation">(</span> data <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> persistence <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> tags <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Our previous problem is that our
protocol
is prone to conflict with naming rules, to handle this we use a slightly more lengthy naming convention. Let’s also implement a function for theprotocol
that can avoid changes when convertingUIImage
toData
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span> <span class="token punctuation">(</span> utf8 <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">pngData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Container</span> <span class="token punctuation">.</span> <span class="token builtin">Error</span> <span class="token punctuation">.</span> failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">return</span> data <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- With the change on our
extension
will avoid making mistakes when reused in different projects. Let’s return to the problem of aContainer
with awrite
method that can handle anyContainerDataConvertible
:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span> <span class="token punctuation">(</span> <span class="token number">_</span> value <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span> permanent <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Tag</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> value <span class="token punctuation">.</span> <span class="token function">asContainerData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- There is still a problem to solve, we need to define more
static
functionprotocol
to make it easier to implement instead of adding everyinstance
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> value <span class="token punctuation">:</span> <span class="token keyword">Self</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> value <span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> value <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> value <span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span> <span class="token punctuation">(</span> value <span class="token punctuation">.</span> utf8 <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span> <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> value <span class="token punctuation">:</span> <span class="token builtin">UIImage</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> value <span class="token punctuation">.</span> <span class="token function">pngData</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Container</span> <span class="token punctuation">.</span> <span class="token builtin">Error</span> <span class="token punctuation">.</span> failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">return</span> data <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- The above code allows us to change each
write
function without adding any complex instances, now we simply callmakeContainerData
directly on eachvalue
:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> write <span class="token operator"><</span> T <span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token number">_</span> value <span class="token punctuation">:</span> T <span class="token punctuation">,</span> persistence <span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span> permanent <span class="token punctuation">,</span> tags <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Tag</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> T <span class="token punctuation">.</span> <span class="token function">makeContainerData</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> <span class="token punctuation">:</span> value <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |