天天看點

iOS 開發中的 Flux 架構模式

<b>本文講的是iOS 開發中的 Flux 架構模式,</b>

<b></b>

在半年前,我開始在 <code>PlanGrid</code> iOS 應用程式中采用 <code>Flux</code> 架構(開發)。這篇文章将會讨論我們從傳統的 <code>MVC</code> 轉換到<code>Flux</code>的動機,同時分享我們目前積累到的經驗。

我嘗試通過讨論代碼來描述我們大部分的 <code>Flux</code> 實作, 它用于我們今天的産品中。 如果你隻對綜合結果感興趣, 請跳過這篇文章的中間部分。

為了引入我們的決定, 我想要先談一談 <code>PlanGrid</code> 這個應用遇到的一些挑戰。一些問題僅針對企業級應用程式,其他應該适用于大部分的 <code>iOS</code> 應用程式。

<code>PlanGrid</code> 是一個相當複雜的 <code>iOS</code> 應用程式。它允許使用者能看到(設計)藍圖并且可以使用不同類型的(标記) 注釋,問題和附件(和很多其他特定工業需要的知識)。

一個重要的方面是, 這個應用程式(可以)先離線使用。無論是否有英特網連接配接,使用者可以使用所有應用程式提供的特性。這意味着我們需要在用戶端存儲許多資料和狀态。我們也需要實施一些本地的業務規則(例如,一個注釋是否可以被使用者删除?)。

<code>PlanGrid</code> 可以在 <code>iPad</code> 和 <code>iPhone</code> 平台上運作,但他的 <code>UI</code> 被優化過,可以充分使用平闆的螢幕。這意味着不像其他<code>iPhone</code> 應用程式,我們常常同時展現多個視圖控制器。這些視圖控制器常常(互相)共享着相當多的狀态。

所有的這些意味着,我們的應用程式花在狀态管理上費了很多努力。任何應用程式結果的變化或多或少都和以下幾點相關:

更新本地對象的狀态。

更新 <code>UI</code>。

更新資料庫。

用隊列儲存變化,它将在有網絡連接配接的情況下發送給伺服器。

當狀态改變時,通知其他對象

盡管我計劃在之後的文章中(更新)包含我們新架構中其他的部分,我今天想把精力集中在第5個. 我們怎麼在應用程式中構成狀态更新(機制)?

這是我們應用程式開發中至關重要的問題(價值十億美元 

iOS 開發中的 Flux 架構模式

)。

大多數 <code>iOS</code> 的工程師,包括 <code>PlanGrid</code> 應用程式早些時候的開發者們,想出了以下答案:

代理機制 (<code>Delegation</code>)

鍵值觀察政策 (<code>KVO</code>)

通知中心 (<code>NSNotificationCenter</code>)

回調代碼塊 (<code>Callback Blocks</code>)

使用資料庫作為來源 (<code>Using the DB as source of truth</code>)

所有這些實作都在不同的情況下有效。然而,這些不同的操作是來源于很多在由發展多年以來的很大的代碼庫中的不一緻。

經典的 <code>MVC</code> 隻提倡分離資料和它的展示層。在缺少其他結構性的指導下,剩下的東西都由個别開發者們決定。

很長一段時間 <code>PlanGrid</code> 應用程式(像其他 <code>iOS</code> 應用程式)都沒有一個定義好的模式去管理狀态。

很多現存的狀态管理工具,例如 <code>delegation</code> 和 <code>blocks</code> 常常去在元件中建立強連接配接(依賴),這并不令人滿意 - 兩個視圖控制器迅速變得強耦合,在嘗試互相分享更新的狀态時。

其他工具,例如,<code>KVO</code> 和 <code>通知</code>, 建立無形的(程式)依賴。将他們使用在大型的代碼庫中,會迅速導緻代碼的變化,引起不可預期的副作用。對于一個控制器來說,想觀察到那些本不需要關心的資料模型層的細節實在是太容易了。

完整的代碼審閱和樣式指導的作用有限,許多架構上的問題都從很小的不一緻性開始并且花很長的時間擴充到許多嚴重的問題。使用意義清晰明确的模式替換,這将會很容易盡早發現差異。

在重構 <code>PlanGrid</code> 應用程式期間,我們最重要的目标是,采用一個清晰的模式和(創造)最佳實作方式。這樣允許未來的新特性能以更加一緻的方式寫入代碼庫,也讓更多新進的工程師提高工作效率。

在我們的應用程式中,狀态管理是複雜度比較大的代碼源之一,是以我們決定去定義一個模式,所有新的特性都會按照這個模式更好的前進。

在遭遇了很多我們代碼中的痛苦後,更加讓我們相信,那些 <code>Facebook</code> (臉書) 在第一次提出 <code>Flux</code> 模式是偶提出的問題:

不可預見性,聯級狀态更新

很難在(多個)元件中明白(互相)依賴關系

複雜的資訊流程

不清晰的真實來源

看上去 <code>Flux</code> 将會非常适合解決很多我們現在遇到的問題。

這個模式能被下圖很好的诠釋,它展示了不同的 <code>Flux</code> 元件:

iOS 開發中的 Flux 架構模式

在 <code>Flux</code> 架構中,<code>store</code> 是應用程式某一個部分的單一真實來源。無論 <code>store</code> 中的狀态何時更新,它都将發送一個變更事件給所有訂閱這個 <code>store</code> (通知/消息)的視圖。

狀态更新(事件)隻會通過 <code>actions</code> 産生。

一個 <code>action</code> 描述了一個預期的狀态的變化,但它不自己實作狀态的改變。所有想要改變狀态的元件,都要發送一個 <code>action</code>給全局的 <code>dispatcher</code>。每當一個 <code>action</code> 被分發,所有有關系的 <code>store</code> 都會收到它。

作為對 <code>actions</code> 的響應,<code>stores</code> 會更新他們的狀态并且通知和這些視圖有關這個新的狀态。

在上圖顯示了,<code>Flux</code> 架構實施的一個單向的資料流。它對以下幾個點嚴格區分:

視圖隻會從 <code>stores</code> 接收資料。每當 <code>store</code> 更新的時候,在視圖上的處理方法會被調用。

視圖隻會通過分發的 <code>actions</code> 改變狀态。 因為 <code>actions</code> 隻描述意圖,業務邏輯從視圖裡隐去了。

<code>store</code> 隻會更新它的狀态,當它接收到 <code>action</code>的時候。

這些限制使得設計,開發和調試一些新的特性變得更加容易。

我們在 <code>PlanGrid iOS</code> 應用程式上的實作和 <code>Flux</code> (官方提供的)參考程式有些不同。我們為每一個 <code>store</code> 實施了一個看得見的 <code>state</code> 屬性。不像原生的 <code>Flux</code> 實作方式,當 <code>store</code> 有更新的時候,我們不發送變更通知。而是視圖觀察 <code>store</code> 中的<code>state</code> 屬性。每當視圖觀察到 <code>state</code> 屬性有變化,他們按照下圖響應(變化):

iOS 開發中的 Flux 架構模式

對于 <code>Flux</code> 參考程式僅有細微的差别,但是提到這個有助于下來部分(的了解)。

有了對 <code>Flux</code> 架構的基本認識,讓我們看看更多實作的細節,和一些當在 <code>PlanGrid</code> 應用程式中實作 <code>Flux</code> 架構,我們需要去回答的問題。

(去定義)每一個 <code>store</code> 的作用域是一個非常又去的問題,每當使用 <code>Flux</code> 模式的時候會經常發生。

由于 <code>Facebook</code> (臉書) 釋出了 <code>Flux</code> 模式,不同的社群開發出了不同的版本。有一個叫做 <code>Redux</code>,通過疊代 <code>Flux</code> 實施了每一個應用程式應該隻有一個 <code>store</code>。這個 <code>store</code> 存儲了整個應用程式的狀态(有很多其他,細微,不同的其他作用域的文章。)

<code>Redux</code> 很受大衆歡迎,因為這個單個 <code>store</code> 的實作方式将大大簡化很多應用程式的架構。在傳統的 <code>Flux</code>,有多個<code>stores</code>,應用程式将會遭遇這個情況,當他們需要結合的狀态,它被存儲在了一個其他的 <code>store</code>中,為了去渲染某一個視圖。這個實作常常重制 <code>Flux</code> 模式嘗試去解決的問題,例如在應用程式中多個元件的複雜依賴。

對 <code>PlanGird</code> 來說,我們決定使用傳統 <code>Flux</code> 而非使用 <code>Redux</code>。我們不确定怎麼将單個 <code>store</code> 存儲整個應用程式的狀态實作到這個龐大的應用程式中。未來,我們認為,我們将使用非常少的内部存儲依賴,讓 <code>Redux</code> 作為一個可選項來說變得不那麼重要了。  

我們已經總結出一個硬性規定有關單獨 <code>store</code> 的作用域。

目前,在我們的代碼庫中,我能識别出的兩個模式:

特性/視圖特定的存儲: 每一個視圖控制器(或者每一個相關聯的視圖控制器)收到它自己的 <code>store</code>。這個 <code>store</code> 模仿了視圖獨特的狀态。 

共享狀态的 <code>stores</code>: 我們有 <code>stores</code> 存儲和管理狀态,被很多視圖共享。我們嘗試保持這些 <code>stores</code> 很小的數量。<code>IssueStore</code> 就是一個這樣的一個 <code>store</code>。它負責管理所有問題,在目前選中的設計藍圖中可見的狀态。這些形式的 <code>stores</code> 本質上來說一個實時更新的資料庫查詢。

我們目前在實作我們第一個_共享形狀态 stores_ 的過程中,并且正在決定基于這些 <code>stores</code> 類型,模拟不同視圖依賴最好的方式。

讓我們深入觀察下構成 <code>Flux</code> 模式特性的細節。

作為貫穿下幾個部分的例子,我們将使用 <code>PlanGrid</code> 應用程式産品中的一個特性。這個特性允許使用者過濾設計藍圖中的注釋:

iOS 開發中的 Flux 架構模式

我們讨論的特性是截圖上左邊展示出的彈出框。

一般來說,我實作一個特性,都由通過決定與其相關的狀态開始。這個狀态展示了 <code>UI</code> 需要了解的所有東西,為了渲染某一個特性。

讓我們深入我們的例子,看看過濾注釋特性的狀态

這個狀态有一攬子的過濾器, 一個目前選擇的過濾器組和一個布爾值标記,顯示了過濾器是否是活動的。

這個狀态為 <code>UI</code> 需求定做。這一攬子過濾器用表視圖渲染。這個被選擇的過濾器組用于顯示/隐藏每一個單獨的過濾器組的細節。<code>isFiltering</code>标記被用于決定去消除所有過濾器的按鈕是否被在 <code>UI</code> 中啟用和關閉。

在決定了某一個特性狀态的模型之後,我常常在下一步考慮不同的狀态變換。在 <code>Flux</code> 架構中,狀态的變換以 <code>actions</code> 形式模拟,描述了什麼樣的改變是預期的。對于注釋過濾器特性,這一攬子 <code>actions</code> 很段:

甚至沒有一個對這個特性深入認識,它也是能被了解的,狀态由 <code>action</code> 來轉換。衆多 <code>Flux</code> 架構中的一個好處是,這個<code>actions</code> 的清單是一個所有狀态改變的全方位的概述,它能被觸發用于這個特别的特性。

我們在這一步中實作這個特性的核心業務邏輯。我個人想使用 <code>TDD</code> 開發方式,我将在之後讨論。這個 <code>store</code> 能用以下内容總結:

用 <code>dispatcher</code> 注冊對所有 <code>actions</code> 感興趣的 <code>store</code>。目前的例子中,它是所有的 <code>AnnotationFilteringActions</code>。

實作一個處理函數,它将被每一個單獨的 <code>actions</code> 調用。

在這個處理函數中,執行必要的業務邏輯和在完成後更新狀态。

最為一個例子,我們能看一下 <code>AnnotationFilterStore</code> 怎麼處理 <code>toggleFilterAction</code>。

這個例子并不是那麼簡單。是以讓我們一點一點分解。每當 <code>ToggleFilterAction</code> 被分發的時候,<code>handleToggleFilterAction</code> 就被調用。<code>ToggleFilterAction</code> 攜帶了哪個具體的過濾器需要被切換的消息。

作為一個實作這個業務邏輯的開端,這個方法簡單地通過切換 <code>filter.enabled</code> 這個值來切換過濾器。

之後,我們對這個特性,實作了一些定制化的業務邏輯。當配合使用那些過濾有問題的注釋的過濾器的時候,我們需要去激活<code>issueTypeFilter</code>。沒有必要深入讨論這個 <code>PlanGrid</code> 特有的特性,但是這個方法封裝了一些和開關觸發器有關的業務邏輯。

在這個方法的結尾,我們調用 <code>_applyFilter()</code> 方法。這是一個共享方法,它被很多 <code>action</code> 處理函數中使用:

調用 <code>self._annotationFilterService.applyFilter()</code> 真正觸發了注釋過濾器在頁面上顯示。過濾器的邏輯本身是有點複雜的,是以把它移動一個獨立的,專門的類型中。

每一個 <code>store</code> 的角色是提供狀态資訊,它與相關的 <code>UI</code> 和成為關聯點為了狀态的更新。這并不意味着整個業務邏輯需要被實作在 <code>store</code> 本身。

每一個 <code>action</code> 處理函數最後一步是更新狀态。在 <code>_applyFilter()</code> 方法中,我們正在更新 <code>isFiltering</code> 狀态值通過檢查是否任何過濾器正在被激活。

還有一個需要注意的事情是有關這個特别的 <code>store</code>: 你可能期望看到一個外部的狀态更新,去更新過濾器的值,它存儲在<code>AnnotationFilterState</code>。一般來說,這是我們如何去實作我們的 <code>stores</code>的方式,但是這個實作方式有一點特别。

由于存在 <code>AnnotationFilterState</code> 中的過濾器需要與很多現存的 <code>Objective-C</code> 代碼互動,我們決定将他們模型成類。這意味着他們是引用類型并且 <code>store</code> 和注釋過濾器 <code>UI</code> 共享一個對同一個執行個體的引用。反過來意味着在<code>store</code>内所有發生在過濾器上的變化,也對 <code>UI</code> 是可見的。一般來說,我們嘗試避免這個,通過隻在我們的狀态結構中使用值類型 - 但是這篇文章是有關真實世界的 <code>Flux</code> 并且在這個特别的例子中,為了讓 <code>Objective-C</code> 互動更容易被接受而妥協。

如果過濾器是值類型,我們需要對更新過的過濾器的值指派到我們的狀态屬性,為了讓 <code>UI</code> 觀察到這個變化。由于我們在這裡使用了引用類型,我們執行一個幽靈狀态更新:

這個對 <code>_state</code> 屬性指派的任務将會開啟更新 <code>UI</code> 的政策 - 一會我們将讨論這個過程的細節。

我們已經深入足夠了解實作的細節了,是以我想暫告這一個部分,并提醒有關高層次 <code>store</code> 的責任:

用 <code>dispatcher</code> 注冊 <code>store</code>,對所有 <code>actions</code> 感興趣的。在目前的例子中,它就是 <code>AnnotationFilterActions</code>。

實作一個處理函數,它将會被每個單獨的 <code>actions</code> 調用。

在這個處理函數中,執行必要的業務邏輯并且在完成後更新狀态。

讓我們移步到讨論 <code>UI</code> 怎麼接收到來自 <code>store</code> 的狀态更新。

每當一個狀态更新(的事件)發生, 自動更新 <code>UI</code> 的機制就被觸發, 這是 <code>Flux</code> 的一個核心理念。它保證了 <code>UI</code> 始終顯示最新的狀态,并且可以擺脫一直需要(手動地)維護這些代碼的工作。這一步類似于在 <code>MVVM</code> 架構中,将一個視圖綁定到<code>ViewModel</code>。

有很多中方式實作這個 - 我們決定在 <code>PlanGrid</code>中,使用 <code>ReactiveCocoa</code> 使得 <code>store</code> 提供一個可見的 <code>state</code> 屬性。下面就是 <code>AnnotationFilterStore</code> 怎麼去實作這個模式的方法:

<code>_state</code> 屬性被用于在 <code>store</code> 内改變狀态。<code>state</code> 屬性被用戶端使用于訂閱 <code>store</code> 的消息。這允許 <code>store</code> 資訊的訂閱者們接收到狀态的更新,但是并不允許他們直接改變狀态。(狀态的改變隻能通過 <code>actions</code> 發生!)。

在初始化中,内部可被觀察的屬性僅僅簡單的綁定到外部信号發生器:

現在,任何 <code>_state</code> 的更新将會自動将最新的狀态值通過信号發生器發送給并且存儲在 <code>state</code> 中。

僅剩下的就是通過代碼确認,每當一個新的 <code>state</code> 值被發出,<code>UI</code> 都更新了。這算得上當開始在 <code>iOS</code> 上使用 <code>Flux</code> 模式最複雜的部分之一了。在網頁上,<code>Flux</code> 能很好的和 <code>Facebook</code> (臉書) 的 <code>React</code> 架構配合。<code>Recat</code> 是為處理以下特性場景而設計的:

當配合 <code>UIKit</code> 時,我們沒有這個至寶,相反我們需要自己手工實作 <code>UI</code> 的更新。我不能在這篇文章裡深入讨論這個實作的細節,否則這篇文章将會太冗長。我們的底線是為 <code>UITableView</code> 和 <code>UICollectionView</code> 建立一些類似于 <code>React API</code> 提供的調用接口,我們将在之後的文章裡提到他們。

讓我們再看看實際的代碼 (我們摘選了部分代碼),從注釋過濾器這個特性中。這段代碼存在于<code>AnnotationFilterViewController</code> 中:

我們在代碼庫中遵循着一個準則,每一個視圖控制器都有一個 <code>_bind</code> 方法,它被 <code>viewWillAppear</code> 調用。這個 <code>_bind</code> 方法負責訂閱 <code>store</code> 的狀态并且提供當狀态變化發生時候,提供更新 <code>UI</code> 的代碼。

由于我們需要我們自己實作部分 <code>UI</code> 更新的代碼并且不能依靠類似 <code>React</code> 的架構,這個方法,一般來說,需要包含描述一個特定的狀态更新如何映射到 <code>UI</code> 更新的代碼。<code>ReactiveCocoa</code> 是非常便利的,因為它提供了很多操作 (<code>skipUntil</code>,<code>take</code>,<code>map</code>,其他。),很容易就能建立這些關系。如果你之前沒有使用過 <code>Reactive</code> 的庫,這些代碼可能會讓你感到困惑 - 這一小部分的 <code>ReactiveCocoa</code> 代碼學起來很快。

在例子中的第一行 <code>_bind</code> 方法確定了,每當一個狀态發生更新的時候,表視圖能獲得這個更新。我們使用 <code>ReactiveCocoa</code> 中<code>ignoreNil()</code> 操作符,來確定我們不會為一個空狀态啟動了更新。之後,我們使用 <code>map</code> 操作符将最新的狀态從 <code>store</code> 中映射到表述圖應該變成什麼樣的描述符。

這個映射通過 <code>annotationFilterViewProvider.tableViewModelForState</code> 方法發生。這也是我們自定義的類似 <code>React</code> 的<code>UIKit</code> 包裝器參與作用的地方。

我不會深入讨論所有的實作細節,但是 <code>tableViewModelForState</code> 方法看上去是這樣的:

這個方法的結果,之後綁定到視圖控制器的 <code>tableViewDataSource</code> 屬性。存儲在這個屬性中的元件,會負責基于<code>FluxTableViewModel</code> 提供的資訊來更新 <code>UITableView</code>。

其他的綁定代碼會比較容易,比如,負責基于 <code>isFiltering</code> 狀态來開啟/關閉 <code>Clear Filter</code> 的按鈕。

實作 <code>UI</code> 綁定的過程是比較複雜的部分之一,由于它不能與 <code>UIKit</code> 的程式設計模型完美配合。但它隻需要花一點精力寫出一些自定義的元件,就能簡單些。從我們的經驗來看,我們通過實作這些自定義元件節省了很多研發時間,而不是一定要保持經典的<code>MVC</code> 實作方式,在那些在多個視圖控制器需要重複實作 <code>UI</code> 更新的地方。

有了這些 <code>UI</code> 的綁定方法,我們讨論實作 <code>Flux</code> 特性的最後一個部分。由于我們已經掌握了很多内容,我想要快速回顧下之前的内容,在我們繼續讨論如何測試這些 <code>Flux</code> 特性之前。

當實作一個 <code>Flux</code> 模式特性的時候,我們需要将工作分為以下幾個部分:

定義狀态類型的形狀。

定義 <code>actions</code>。

實作業務邏輯和針對每個 <code>action</code> 狀态的轉變 - 這個實作在 <code>store</code> 中。

實作 <code>UI</code> 綁定方法,将狀态映射到視圖展示層。

這些已經包括了所有我們讨論過的有關實作的細節。

讓我們繼續讨論如何測試 <code>Flux</code> 特性。

有一個 <code>Flux</code> 主要的好處是,它把有關的内容嚴格的區分開。這讓測試業務邏輯和大塊的 <code>UI</code> 代碼變得非常容易。

每一個 <code>Flux</code> 特性都有兩個重要的區域需要被測試:

在 <code>store</code> 中的業務邏輯

視圖模型的提供者 (就是那些我們實作的類似 <code>React</code> 的方法,他們基于輸入的狀态描述了 <code>UI</code>。)

測試 <code>stores</code> 很簡單。我們能通過插入 <code>actions</code> 到 <code>stores</code> 驅動互動,并且我們能通過訂閱 <code>store</code> 或者觀察在我們測試用的内部 <code>_state</code> 屬性來觀察狀态的變化。

另外,我們能模拟其他外部的類型,那些,<code>store</code> 可能需要去互動的内容,為了實作某一個特性(可能是一個 <code>API</code> 的用戶端或者資料對象)并且在 <code>store</code> 的初始化器中注入這些。這允許我們去驗證,那些類型是否被如期調用。

在 <code>PlanGrid</code>中,我們使用 <code>Quick</code> 和 <code>Nimble</code> 以反應樣式來寫測試代碼。以下是一個簡單的例子,來自于注釋過濾器,儲存某一個 <code>action</code>:

再一次強調,有關測試 <code>stores</code> 将會被放在其他文章裡,是以我們也不會深入讨論過多細節。然而,測試的方式已經很清楚了。我們把 <code>actions</code> 發送給 <code>store</code>并且驗證響應,以改變狀态或者模拟注入代碼的形式。

(你會對為什麼我們在 <code>store</code> 中調用 <code>_handleActions</code>,而非使用 <code>dispatcher</code> 來配置設定感到好奇。起初,我們使用異步<code>dispatcher</code>,當有 <code>actions</code> 需要被配置設定時,這也意味着我們的測試方法也需要是異步調用的。是以,我們直接在 <code>store</code> 中直接調用處理函數。是以,這個 <code>dispatcher</code> 的實作方式也變了,是以我們在我們的測試中使用 <code>dispatcher</code>。 )

當實作 <code>store</code> 中的業務邏輯的時候,我總會先寫我的測試代碼。我們的代碼結構能很好的配合 <code>TDD</code> 開發過程。

<code>Flux</code> 架構結合我們申明的 <code>UI</code> 層能讓測試視圖變得非常容易。我們也一直在内部讨論有關我們想要覆寫(測試)多少的視圖的話題。

實際上,所有我們的視圖代碼都是相當清晰的。它綁定了在 <code>store</code> 中的狀态到我們不同 <code>UI</code> 層的屬性上。對于我們的應用程式,我們決定通過 <code>UI</code> 自動測試機制來覆寫我大部分的代碼。

對于我們的應用程式,我們的 <code>UI</code> 自動測試已經足夠了,是以我們不需要其他的快照測試。

我們也嘗試使用單元測試在我們的視圖方法上(例如,早些時候我們看到的 <code>tableViewModelForState</code> 方法)。這些視圖提供者,映射一個狀态到 <code>UI</code> 描述符,是以他們能基于輸入和傳回值被很容易的測試,我發現,這些測試并不能增加很多價值,因為他們僅僅是複制了申明過的實作了的描述符。)

使用 <code>Flux</code> 架構在視圖測試熵變得非常簡單,因為視圖的代碼獨立于其他的應用程式的實作。你隻需要注入一些狀态,他們應該被反映在你的測試中,并且他們處理的很好。

就如我們所見,的确有很多其他的方法可以測試 <code>UI</code>,我對我們(其他開發者),從長遠來看,會選擇哪一個很感興趣。

在我們深入讨論了那麼多的實作細節之後,我想總結下目前我們的經驗和教訓。

我們隻使用了 <code>Flux</code> 架構 6 個月左右,但是我們已經能看到很多給我們代碼庫帶來的好處:

新的特性能被一緻性的實作。貫穿于多個特性間的,<code>stores</code> , 視圖提供者和視圖控制器的結構幾乎保持一緻(完全相同)。

通過監視狀态,<code>actions</code> 和 <code>TDD</code> 的測試架構,幾分鐘之内,就能很容易的了解,某一個特性是怎麼工作得。

我們很好的分離了 <code>stores</code> 和視圖之間的關系。對于某個代碼是否應該存在沒有模糊的界定。

我們的代碼閱讀起來很簡單。狀态和視圖之間的以來關系總是非常明确。這也讓調試工作輕松愉快。

所有以上的優點,都讓新來的開發者門更容易上手工做。

顯而易見,我們也遇到了一些痛點:

首先,內建 <code>UIKit</code> 元件有一點麻煩。不像 <code>React</code> 元件, <code>UIKit</code> 視圖不提供 <code>API</code> 基于一個新的狀态容易的更新自己。這部分的工作完全依賴與我們自己,我們需要實作手動綁定視圖的工作或者自定義的元件,對 <code>UIKit</code> 二次開發。

我們需要想出一個在大型應用程式中共享狀态的好的模式,(如文章一開始讨論的,"什麼是 <code>Store</code> 的作用域?")。我們需要在原版的 <code>Flux</code> 架構中,增加 <code>stores</code> 之間的依賴關系麼? 我們還有其他選擇麼?

還有很多,很多的實作細節,優點和缺點,我想我會在之後的文章裡深入讨論他們。

至此,我對目前的選擇很滿意,并且我希望這篇文章能給你們一些參考,是否 <code>Flux</code> 架構也對你适合。

最後,如果你對 <code>Flux</code> 在 <code>Swift</code> 上的實作感興趣,或隻想為我們的産品貢獻一份你的力量共同成就一個巨大的産業。

<b>原文釋出時間為:2016年07月28日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>