什麼是開發 App 的正確步驟?

故胤道長發表於2017-01-16

在 iOS 開發中,寫一個 App 很容易,但是要寫好一個 App,確是要下另一番功夫。首先,我們來看一個 App 的開發要求:

寫一個 App,顯示出 Spotify 上 Lady Gaga 相關的所有音樂專輯,相關資訊可以通過以下網址查到:
https://api.spotify.com/v1/search?q=lady+gaga&type=album

需求分析

首先拿到開發要求,最重要的是明確開發細節。這裡面有很多我們不清楚的地方需要與產品經理和設計師交流:顯示是要用 TableView 還是 CollectionView?每個音樂專輯的哪些資訊需要顯示?如果專輯數量過多,我們優先顯示哪些專輯?這個 App 除了顯示資訊以外,還需要哪些擴充功能?這個產品的大小是否有要求?需要多少天完成?

經過討論之後,大家的一致意見是做個如下的 App:

什麼是開發 App 的正確步驟?
LadyGaga

於是我們就清楚了,是要做一個 tableView,每個 Cell 對應一個專輯資訊,左邊是圖片,右邊是專輯名。點選 Cell,可以看到相應的專輯大圖。

構建架構

首先這個 App 比較簡單,我們只要用最基本的 MVC 就可做好。

  • Model 部分:
    只需要一個 Model, 為 Album,對應每一個專輯的資訊;
  • View 部分:
    主體的部分可以在 Storyboard 裡面完成;
    最好單獨新建一個 UITableViewCell 的子類,用來對應設定專輯的UI;
  • ViewController 部分:
    其中一個 ViewController 為 TableViewController,負責現實所有專輯的資訊;
    另一個 ViewController 負責展示 detail info,比如專輯的大圖;
  • Network 部分:
    負責從網路上 fetch 專輯資訊;以及根據專輯的圖片網址,fetch 圖片資料;
121721232-6f63a76508c7afb2
基本架構

細節實現

  • Network 部分:

fetchAlbums 和 downloadImage 都用Apple 自帶的 URLSession 和 JSONserialization 就可以實現,或者也可以用優秀的第三方庫 AlamoFire。因為這個 App 比較簡單,AlamoFire 優勢不明顯,且引入第三方庫會增加 App 的體積,故而推薦使用前者。基本上就是實現下面兩個函式:

對於第一個函式 fetchAlbums,因為網路請求是耗時耗力的工作,我們一般會將它們用後臺執行緒而非主執行緒(UI執行緒)來處理,這樣可以保持UI的流暢執行。用閉包則是為了非同步多執行緒完成後可以回撥,同時 error 是為了監視網路請求是否出錯。
對於第二個函式 downloadImage,最簡單的方法是通過 url 拿到對應的 data,然後通過相應的 data 拿到 image。返回為 optional 的原因是有可能 URL 有問題或者網路請求出錯,此時返回 nil。
從API設計的角度來說,以上的downloadImage並不是最佳設計。最佳的設計是我們能知道哪裡出錯了,比如下面這樣:

  • ViewController 部分:

對於 AlbumsController,我們用到了代理模式(Delegate),即將 tableView 代理到了 AlbumsController 上。我們只要實現相應的 dataSource 和 delegate 方法即可。其中對於 dataSource 而言,有兩個方法是必須實現的,它們是:

同時,AlbumsController 裡面,還有兩個陣列,一個用來裝專輯([Album]),一個用來裝圖片([UIImage?]),這樣我們只需下載資料一次,並將其存入相應陣列,之後就無需再次進行相關的網路請求了。也就是說,這兩個陣列起到了快取的作用。

具體的實現是:首先在 viewDidLoad() 中請求伺服器取出相應的資料。之後根據專輯數量設定 TableView 的相應行數。在具體的一行當中,我們可以根據 indexPath 確定相應的專輯。根據相應專輯的圖片 URL ,我們可以拿到相應的圖片,之後快取進圖片陣列。由於我們複用了 TableView 的 Cell,所以如果不快取圖片而每次去進行網路請求,會因為延時很嚴重而會造成圖片閃爍的後果。

最後兩個 ViewController 之間的跳轉可以用 navigationController 來實現。

  • View 部分:

自定義 AlbumCell 可以保證 App 的擴充套件性很好。同時,為了處理有些專輯名字過長 Label 顯示不了的問題,可以用 autoshrink 來處理。

131721232-4d4e695904c573e3
App 執行流程

優化擴充

上面的設計和實現比較理想化,現在我們要考慮一個邊界情況,假如網路不穩定,怎麼辦?
一個簡單的解決方法就是,當網路好的時候把資料下載下來,存入 cache 和 storage 中,之後即使網路中斷、App 崩潰,我們都能從 storage 中拿到相應資料。
這裡引入外觀模式(Facade),建立一個新的 class 名為 LibraryAPI,提供兩個介面:

這裡的方法跟之前 Network 的不同之處在於:getAlbums 方法會先嚐試從 storage 中取出相應資料,如果沒有,則去訪問 Network,之後再把從 Network 中拿到的值存入 storage 中。這裡面的實現有點複雜,牽涉到兩大模組和多執行緒操作,但是我們並不必關心方法內部的實現,而僅僅關心介面,這就是外觀模式的優點。同時,LibraryAPI 這個 class 最好用單例模式(singleton),因為它應該被當做是全域性 API 被各個 ViewController 來訪問,同時這樣設計也節省資源。

141721232-8b05f96d2fb37cd9
優化後的 App 流程

另外一個優化點在於,假如我們一開始拿到很多資料 —— 例如10000 個專輯,那麼我們該怎麼操作?

正確的做法是分頁。我們可以先只拿20個,顯示在 TableView 上。當使用者快滑到底端的時候,我們可以再取下面20個,然後我們總共有40個在記憶體中可以顯示,以此類推。這樣做的好處是,我們無需下載所有的資料,以最快、最流暢的方式佈局 TableView,同時根據使用者的需求增加相應的專輯資料。

最後一個優化點在於,假如使用者上下滑動很快,我們如何能夠用最快速度載入圖片?

答案是用 operationQueue 來處理,當前 cell 是可見的時候,我們就 resume 下載圖片的程式,否則就 suspend。這樣保證了我們用有限的記憶體和 CPU 去最高效的下載使用者需要、當前要見的圖片。

值得一提的是,大家還可以借鑑 ASDK 的思路來進一步優化程式。

總結

本文從一個簡單的 tableView App 說起,談論了開發一個 App 的4個步驟:需求分析、構建架構、細節實現、優化擴充。簡單介紹了多執行緒和幾種設計模式,希望對大家有所幫助。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

什麼是開發 App 的正確步驟? 什麼是開發 App 的正確步驟?

相關文章