Extension
cho phép chúng ta có thể add thêmfunctionality
cho cáctype
hayprotocol
có sẵn hay một số phần của thư việnApple SDK
có sẵn hoặc thậm chí là thành phần trong cácthird party package
mà chúng ta sử dụng trongproject
.Tuy nhiên
extension
trongSwift
có thể được sử dụng với nhiều cách linh hoạt và nâng cao hơn nhiều so với việc chỉ sử dụng để add thêm cácproperty
haymethod
cho cácexternal object
. Ở bài viết này chúng ta cùng nghiên cứu các cách sử dụng đó để có thể sử dụng trong cácproject
sắp tới.
1/ Thêm các tính năng vào Type tồn tại sẵn:
- Bắt đầu từ đơn giản ta có cách sử dụng
extension
để thêm mới, tùy chỉnh cácAPI
cho các dạngtype
thành phần trongApple system
ví dụ như cácApple standard library
. Ví dụ cụ thể như chúng ta cần làm việc trên mộtapp
màlogic
xử lý yêu cầu chúng ta cần phảiaccess
vào cácelement
trong các mảng khác nhau, để tránh việc luôn phải kiểm traindex
của cácelement
mà chúng taaccess
thì chúng ta có thể làm như sau:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">extension</span> <span class="token builtin">Array</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">element</span><span class="token punctuation">(</span>at index<span class="token punctuation">:</span> <span class="token builtin">Int</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Element</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> index <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">,</span> index <span class="token operator"><</span> <span class="token builtin">count</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">self</span><span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Rất đơn giản nhưng tiện lợi đúng không? Bây giờ chúng ta đã có thể sử dụng
method
trên ở bất kỳArray
nào trongproject
. Chưa dừng ở đó chúng ta còn có thể thực hiện những tùy chỉnh tốt hơn với việc sử dụngextension
choprotocol
RandomAccessCollection
:RandomAccessCollection
định nghĩa các yêu cầu màcollection
cung cấprandom access
cho cácelement
. Mở rộngprotocol
này chúng ta sẽ sử dụngmethod
mới cho bất kìcollection
nào bao gồm cảArray
:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">extension</span> <span class="token builtin">RandomAccessCollection</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">element</span><span class="token punctuation">(</span>at index<span class="token punctuation">:</span> <span class="token builtin">Index</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Element</span><span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token builtin">indices</span><span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>index<span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token constant">nil</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">self</span><span class="token punctuation">[</span>index<span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Chỉ với thay đổi trên, chúng ta bây giờ có thể sử dụng
method
mới cho cáctype
nhưArray
,ArraySlice
,Range
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token comment">// Extracting an optional element from an Array</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> fifthElement <span class="token operator">=</span> array<span class="token punctuation">.</span><span class="token function">element</span><span class="token punctuation">(</span>at<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// Doing the same thing, but using an ArraySlice instead:</span> <span class="token keyword">let</span> slice <span class="token operator">=</span> array<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token operator"><</span><span class="token number">3</span><span class="token punctuation">]</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> secondElement <span class="token operator">=</span> slice<span class="token punctuation">.</span><span class="token function">element</span><span class="token punctuation">(</span>at<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// We could also use our new method with types like Range:</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> thirdValue <span class="token operator">=</span> range<span class="token punctuation">.</span><span class="token function">element</span><span class="token punctuation">(</span>at<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> |
Việc sử dụng
extension
choprotocol
mang lại cho chúng ta sự linh hoạt hơn trong việc sử dụng cácmethod
vàproperty
mà chúng ta thêm vào.Tuy nhiên không phải tất cả
extension
chúng ta thêm vào đều hướng tới mục đích chung trên. Trong một số trường hợp chúng ta cần thêm cácconstraints
để cácextension
thêm cụ thể.Chúng ta cùng xem một ví dụ mà chúng thêm thêm
extension
để addmethod
giúp chúng ta tính toán tổng giá tiền của cácproducts
với cách sử dụngsame type constraints
để đảm bảomethod
sẽ được chỉ được gọi khiSequence
conform
type củaProduct
value:
1 2 3 4 5 6 7 8 | <span class="token keyword">extension</span> <span class="token builtin">Sequence</span> <span class="token keyword">where</span> <span class="token builtin">Element</span> <span class="token operator">==</span> <span class="token builtin">Product</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">totalPrice</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">Int</span> <span class="token punctuation">{</span> <span class="token function">reduce</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> price<span class="token punctuation">,</span> product <span class="token keyword">in</span> price <span class="token operator">+</span> product<span class="token punctuation">.</span>price <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Điều hữu dụng của
constraints
này là nó không chỉ tham chiếu và đảm bảo type cho protocol mà còn có thể sử dụng trongclosure
như sau:
1 2 3 4 5 6 7 8 | <span class="token keyword">extension</span> <span class="token builtin">Sequence</span> <span class="token keyword">where</span> <span class="token builtin">Element</span> <span class="token operator">==</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">Void</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">callAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> forEach <span class="token punctuation">{</span> closure <span class="token keyword">in</span> <span class="token function">closure</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> |
- Ở Swift 5.3, tính năng này còn được nâng cấp hơn cho phép chúng ta có thể sử dụng
constraints
này để cá nhân hóa cách khai báomethod
cho các type được sử dụng để bao đóng nó.
1 2 3 4 5 6 7 8 | <span class="token keyword">extension</span> <span class="token builtin">Sequence</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> callAll<span class="token operator"><</span>T<span class="token operator">></span><span class="token punctuation">(</span>with input<span class="token punctuation">:</span> T<span class="token punctuation">)</span> <span class="token keyword">where</span> <span class="token builtin">Element</span> <span class="token operator">==</span> <span class="token punctuation">(</span>T<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Void</span> <span class="token punctuation">{</span> forEach <span class="token punctuation">{</span> closure <span class="token keyword">in</span> <span class="token function">closure</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Method
trên có thể trở nên hữu dụng khi chúng ta muốn truyền cácsame value
cho cácclosure
khác nhau như trong ví dụ cácorder
sẽnotify
cho tất cảobserver
thuộcObservable
type cóvalue
thay đổi:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">class</span> <span class="token class-name">Observable</span><span class="token operator"><</span><span class="token builtin">Value</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">var</span> value<span class="token punctuation">:</span> <span class="token builtin">Value</span> <span class="token punctuation">{</span> <span class="token keyword">didSet</span> <span class="token punctuation">{</span> observations<span class="token punctuation">.</span><span class="token function">callAll</span><span class="token punctuation">(</span>with<span class="token punctuation">:</span> value<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">var</span> observations <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">(</span><span class="token builtin">Value</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Void</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> |
2/ Tổ chức lại các API và cách tuân thủ Protocol:
Extension
thường được dùng trong việc tổ chức code trongproject
, đây là một tính năng mà chúng ta đã thực hiện nhiều trênObjective-C
. Chúng ta sử dụng để nhóm cácAPI
có cùng chức năng mà nó cung cấp để dễ dàng cho việc tìm kiếm cũng như xếp tính năng.Trong
Swift
chúng ta sử dụng cùng một cách tiếp cận để xếp cácAPI
theoaccess level
. Lấy ví dụ nhưPublish
, chúng ta có một trình khởi tạo đểbuild
mỗiwebsite
mà trong đóSection
dùng như type để các nhóm được gom vào phải tuân thủ theo nhưpublic
,internal
,private
:
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">Section</span><span class="token operator"><</span><span class="token builtin">Site</span><span class="token punctuation">:</span> <span class="token builtin">Website</span><span class="token operator">></span><span class="token punctuation">:</span> <span class="token builtin">Location</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">let</span> id<span class="token punctuation">:</span> <span class="token builtin">Site</span><span class="token punctuation">.</span><span class="token builtin">SectionID</span> <span class="token keyword">public</span> <span class="token keyword">private</span><span class="token punctuation">(</span><span class="token keyword">set</span><span class="token punctuation">)</span> <span class="token keyword">var</span> items <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token builtin">Item</span><span class="token operator"><</span><span class="token builtin">Site</span><span class="token operator">></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> <span class="token keyword">public</span> <span class="token keyword">extension</span> <span class="token builtin">Section</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">item</span><span class="token punctuation">(</span>at path<span class="token punctuation">:</span> <span class="token builtin">Path</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Item</span><span class="token operator"><</span><span class="token builtin">Site</span><span class="token operator">></span><span class="token operator">?</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">func</span> <span class="token function">items</span><span class="token punctuation">(</span>taggedWith tag<span class="token punctuation">:</span> <span class="token builtin">Tag</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">[</span><span class="token builtin">Item</span><span class="token operator"><</span><span class="token builtin">Site</span><span class="token operator">></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><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">internal</span> <span class="token keyword">extension</span> <span class="token builtin">Section</span> <span class="token punctuation">{</span> <span class="token keyword">mutating</span> <span class="token keyword">func</span> <span class="token function">addItem</span><span class="token punctuation">(</span><span class="token number">_</span> item<span class="token punctuation">:</span> <span class="token builtin">Item</span><span class="token operator"><</span><span class="token builtin">Site</span><span class="token operator">></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> <span class="token keyword">private</span> <span class="token keyword">extension</span> <span class="token builtin">Section</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">mutating</span> <span class="token keyword">func</span> <span class="token function">rebuildIndexes</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> <span class="token punctuation">}</span> |
Ngoài lợi ích trong việc tổ chức
code
, cách làm trên chúng ta còn không phải cấp chomethod
cácaccess level
mà mỗiAPI
sẽ tự động được cung cấp các truy cập cần thiết.Cách triển khai trên hoàn toàn có thể áp dụng cho
protocol
, chúng ta có thể kèm theo điều kiện phù hợp cho từngextension
chúng ta thêm, ví dụ như chúng ta tạoListViewController
conform
UITableViewDelegate
quaextension
:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">extension</span> <span class="token builtin">ListViewController</span><span class="token punctuation">:</span> <span class="token builtin">UITableViewDelegate</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">tableView</span><span class="token punctuation">(</span><span class="token number">_</span> tableView<span class="token punctuation">:</span> <span class="token builtin">UITableView</span><span class="token punctuation">,</span> didSelectRowAt indexPath<span class="token punctuation">:</span> <span class="token builtin">IndexPath</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> item <span class="token operator">=</span> items<span class="token punctuation">[</span>indexPath<span class="token punctuation">.</span>item<span class="token punctuation">]</span> <span class="token function">showDetailViewController</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> item<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> |
- Ở đây chúng ta đơn giản tạo ra các điều kiện
wrapper type
để thỏa mãnconform
choprotocol
nhưEquatable
vàHashable
chỉ khiWrapped
cũng thỏa mãn type của cácprotocol
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token comment">// The compiler can still automatically generate the code required</span> <span class="token comment">// to conform to protocols like Equatable and Hashable even when</span> <span class="token comment">// adding those conformances through extensions:</span> <span class="token keyword">extension</span> <span class="token builtin">NetworkResponse</span><span class="token punctuation">:</span> <span class="token builtin">Equatable</span> <span class="token keyword">where</span> <span class="token builtin">Wrapped</span><span class="token punctuation">:</span> <span class="token builtin">Equatable</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">NetworkResponse</span><span class="token punctuation">:</span> <span class="token builtin">Hashable</span> <span class="token keyword">where</span> <span class="token builtin">Wrapped</span><span class="token punctuation">:</span> <span class="token builtin">Hashable</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// Most protocols will probably require us to write some form of</span> <span class="token comment">// bridging code ourselves, though. For example, here we make our</span> <span class="token comment">// network response use its wrapped type's description when it's</span> <span class="token comment">// being converted into a string, rather than defining its own:</span> <span class="token keyword">extension</span> <span class="token builtin">NetworkResponse</span><span class="token punctuation">:</span> <span class="token builtin">CustomStringConvertible</span> <span class="token keyword">where</span> <span class="token builtin">Wrapped</span><span class="token punctuation">:</span> <span class="token builtin">CustomStringConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> description<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">{</span> result<span class="token punctuation">.</span>description <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
3/ Chuyên môn hóa việc sử dụng generic:
Ở điểm cuối cùng, chúng ta cùng xem cách
extension
có thể được sử dụng để chuyên môn hóa cáctype
cũng nhưprotocol
chung cho từng trường hợp sử dụng thực thế:Như
Sequence
vàRandomAccessCollection
protocol
mà chúng ta đã mở rộng để thuận tiện cho việc sử dụng như cách vàiApple Framework
thường sử dụnggeneric
để làm cho cácAPI
trờ nên an toàn và dễ mở rộng hơn. TrongCombine
cácpublisher
đượcimplement
để sử dụngPublisher
protocol
bao gồm các cácgeneric
type được định nghĩa đểOutput
hayFailure
được tạo ra khi cácPublisher
emit.Các
generic
type đó cho phép chúng ta triển khai hoàn chỉnh cácCombine
operator
như việc sử dụngResult
chovalue
được trả về từpublisher emit
:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">extension</span> <span class="token builtin">Publisher</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">asResult</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">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Result</span><span class="token operator"><</span><span class="token builtin">Output</span><span class="token punctuation">,</span> <span class="token builtin">Failure</span><span class="token operator">></span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token builtin">Result</span><span class="token punctuation">.</span>success<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword">catch</span> <span class="token punctuation">{</span> error <span class="token keyword">in</span> <span class="token function">Just</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token function">eraseToAnyPublisher</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Extension
trên cho phéo chúng ta triển khaiCombine
giống vớiAsyncValue
với việcOutput
đượcassign
trực tiếp choResult
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">class</span> <span class="token class-name">AsyncValue</span><span class="token operator"><</span><span class="token builtin">Value</span><span class="token punctuation">:</span> <span class="token builtin">Decodable</span><span class="token operator">></span><span class="token punctuation">:</span> <span class="token builtin">ObservableObject</span> <span class="token punctuation">{</span> @<span class="token builtin">Published</span> <span class="token keyword">private</span><span class="token punctuation">(</span><span class="token keyword">set</span><span class="token punctuation">)</span> <span class="token keyword">var</span> result<span class="token punctuation">:</span> <span class="token builtin">Result</span><span class="token operator"><</span><span class="token builtin">Value</span><span class="token punctuation">,</span> <span class="token builtin">Error</span><span class="token operator">></span><span class="token operator">?</span> <span class="token keyword">private</span> <span class="token keyword">var</span> cancellable<span class="token punctuation">:</span> <span class="token builtin">AnyCancellable</span><span class="token operator">?</span> <span class="token keyword">func</span> <span class="token function">load</span><span class="token punctuation">(</span>from url<span class="token punctuation">:</span> <span class="token constant">URL</span><span class="token punctuation">,</span> using session<span class="token punctuation">:</span> <span class="token builtin">URLSession</span> <span class="token operator">=</span> <span class="token punctuation">.</span>shared<span class="token punctuation">,</span> decoder<span class="token punctuation">:</span> <span class="token builtin">JSONDecoder</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 punctuation">{</span> cancellable <span class="token operator">=</span> session<span class="token punctuation">.</span><span class="token function">dataTaskPublisher</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> url<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">decode</span><span class="token punctuation">(</span>type<span class="token punctuation">:</span> <span class="token builtin">Value</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">,</span> decoder<span class="token punctuation">:</span> decoder<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">asResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span>sink <span class="token punctuation">{</span> <span class="token punctuation">[</span><span class="token keyword">weak</span> <span class="token keyword">self</span><span class="token punctuation">]</span> result <span class="token keyword">in</span> <span class="token keyword">self</span><span class="token operator">?</span><span class="token punctuation">.</span>result <span class="token operator">=</span> result <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Cách làm trên với các
constraints
cho chúng ta tận dụng khả năng suy luận type mạng mẽ củaSwift
cũng như cách màSwiftUI
sử dụng các API để built các view hiển thị.Lấy ví dụ như việc chúng ta làm việc trên
IconView
được render vớiicon
đã được xác định trước. Để tiện cho việc tạoButton
bao gồmicon
chúng ta có thể thêmextension
mà sử dụngtype constraints
làLabel
để định nghĩacontent
màButton
đó đượcrender
:
1 2 3 4 5 6 7 8 | <span class="token keyword">extension</span> <span class="token builtin">Button</span> <span class="token keyword">where</span> <span class="token builtin">Label</span> <span class="token operator">==</span> <span class="token builtin">IconView</span> <span class="token punctuation">{</span> <span class="token keyword">init</span><span class="token punctuation">(</span>icon<span class="token punctuation">:</span> <span class="token builtin">Icon</span><span class="token punctuation">,</span> action<span class="token punctuation">:</span> @escaping <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Void</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span><span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span>action<span class="token punctuation">:</span> action<span class="token punctuation">,</span> label<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">IconView</span><span class="token punctuation">(</span>icon<span class="token punctuation">:</span> icon<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> |
- Và giờ khi chúng ta sử cụng
API
trên để tạoinstance
Button
thìcomplier
sẽ tự động thêm thông báo cho chúng ta muốn sử dụngIconView
như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">struct</span> <span class="token builtin">ProductView</span><span class="token punctuation">:</span> <span class="token builtin">View</span> <span class="token punctuation">{</span> @<span class="token builtin">ObservedObject</span> <span class="token keyword">var</span> viewModel<span class="token punctuation">:</span> <span class="token builtin">ProductViewModel</span> <span class="token keyword">var</span> body<span class="token punctuation">:</span> some <span class="token builtin">View</span> <span class="token punctuation">{</span> <span class="token builtin">VStack</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token function">Button</span><span class="token punctuation">(</span>icon<span class="token punctuation">:</span> <span class="token punctuation">.</span>shoppingCart<span class="token punctuation">)</span> <span class="token punctuation">{</span> viewModel<span class="token punctuation">.</span><span class="token function">performPurchase</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> |