2015/04/22更新:最新更新到Xcode6.3和Swfit 1.2。
更新日志:這個教程由Vicent Ngo最新更新到iOS 8和Swift。原教程由教程組組員Eli Ganem發表。
iOS設計模式- 你很可能聽說過這個詞,但是你真的知道它什么意思么?雖然大多數開發者都同意設計模式非常重要,但于此同時,并沒有很多文章寫這個內容,而且我們作為開發者在寫代碼時也有時不在意設計模式。
設計模式是在軟件設計中常見問題的重復使用的解決方法。它們是可以讓你的代碼清晰易懂可重復使用度高的模版。它們也可以幫你完成一些松散的代碼,你不用很麻煩就可以更改代碼或者替換里面的組成部分。
如果你并不熟悉設計模式,那我有一個好消息給你!首先, 由于Cocoa的建立方式,你現在就可以使用很多iOS的設計模式了!其次,這個教程會讓你快速學所有的主流(以及不那么主流)的被廣泛應用在Cocoa里的iOS設計模式。
教程分為兩部分,你將會做一個音樂庫的app,它可以顯示你的專輯以及它們相關的信息。
在開發這個app的過程中,你將會了解大部分常見的Cocoa設計模式:
創建型:單例模式(Singleton)
結構型:MVC,修飾模式(Decorator),適配器模式(Adapter),外觀模式(Facade)
行為型:觀察者模式(Observer),備忘錄模式(Memento)
別以為這篇文章是關于理論的;你將會在你的音樂app中知道怎么運用大多數這些設計模式。在本教程最后你的app最終會是這樣:
讓我們開始吧!
下載并解壓起始項目, 在xCode里打開BlueLibrarySwift.xcodeproj.
在這個項目里,有三件事需要注意:
ViewController有兩個IBOutlet連接了表格(table view)和storyboard的工具欄
為了讓你更方便,storyboard里已經有三個設置好限制的部分了。最上面的部分是顯示專輯封面的,在它下面會有一個表格顯示對應專輯封面的信息。最后那個工具欄有兩個按鈕,一個可以撤銷操作,另一個可以刪除你所選擇的專輯。Storyboard如下所示:
3.一個還沒寫的HTTP Client類 (HTTPClient)需要你之后完成。
注意:你知道當你新建一個Xcode項目時,你的代碼已經有設計模式了么?MVC,代理,原型,單例 - 你可以免費得到它們所有!
在你深入第一種設計模式之前,你需要先新建兩個類來存以及展示專輯數據。
去到“File\New\File…”(或者直接按Command+N)。選擇iOS > Cocoa Touch Class然后點擊Next。將類的名字設置成Album,子類設置成NSObject。最后選擇Swift語言。點擊Next和Create。
打開Album.swift并且在類定義里加上以下這些屬性。
var title : String!
var artist : String!
var genre : String!
var coverUrl : String!
var year : String!
這樣你就建立了五個屬性。Album類會記錄標題,藝術家,類別,專輯封面,以及專輯年份。 接下來在屬性下面加上對象初始化方法。
init(title: String, artist: String, genre: String, coverUrl: String, year: String) {
super.init()
self.title = title
self.artist = artist
self.genre = genre
self.coverUrl = coverUrl
self.year = year
}
這段代碼給Album類創建了初始化方法。當你創建一個新的專輯時,你需要傳給它專輯名稱,藝術家,種類,封面URL和年份。
接下來加上以下這段代碼:
override var description: String {
return "title: \(title)" +
"artist: \(artist)" +
"genre: \(genre)" +
"coverUrl: \(coverUrl)" +
"year: \(year)"
}
description()方法返回一個代表專輯屬性的字符串。
再次去File\New\File…,選擇Cocoa Touch Class并且點擊Next。將這個類的名稱改為AlbumView,不過這次將它的子類設置成UIView。確認語言為Swift然后點擊Next和Create。
打開AlbumView.swift,然后在這個類的定義里加上以下屬性:
private var coverImage: UIImageView!
private var indicator: UIActivityIndicatorView!
coverImage代表了專輯封面圖片。第二個屬性是指示物,表明專輯封面正在下載這么一個活動。
這些屬性被設置成private因為沒有除了AlbumView的類需要知道這些屬性的存在;它們只需要在這個類的功能里面被用到。當你要建立一個庫給別的開發者用,你要把隱藏的狀態信息隱藏,這個慣例是非常重要的。
接下來,給這個類加上初始化方法:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
init(frame: CGRect, albumCover: String) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
backgroundColor = UIColor.blackColor()
coverImage = UIImageView(frame: CGRect(x: 5, y: 5, width: frame.size.width - 10, height: frame.size.height - 10))
addSubview(coverImage)
indicator = UIActivityIndicatorView()
indicator.center = center
indicator.activityIndicatorViewStyle = .WhiteLarge
indicator.startAnimating()
addSubview(indicator)
}
The `NSCoder` initializer is required because UIView conforms to NSCoding.
`NSCoder`初始化方法是必須的,因為UIView遵守NSCoding.
`commonInit`是一個這兩個初始化里都被用到的幫助方法:你將在這個項目的剩余部分也用到它,你將會給專輯界面設置一些好的默認值。把背景設置為黑,建立一個5像素邊框的圖片界面,然后建立并添加到活動指示器。
最后,添加一下方法:
func highlightAlbum(#didHighlightView: Bool) { if didHighlightView == true { backgroundColor = UIColor.whiteColor() } else { backgroundColor = UIColor.blackColor() } }
如果專輯被高亮,它將設置專輯的背景色為白,如果沒有被高亮,則被設置成黑。
建造你的項目(`Command+B`)來確認所有東西都正常。都好了?那么就準備好來做你第一個設計模式吧!:]
MVC - 設計模式之王

MVC是Cocoa中的一個組成部件,它無可厚非是所有設計模式中最常用的。它將對象按照你程序的角色分類,并且根據角色清晰地分離代碼。
這三個角色為:
Model: 這個對象存著你程序的數據并且定義怎么控制它。舉個例子,在你的程序中,專輯這個類就是Model。
View: 這個對象負責Model的視覺描述以及用戶可以交互的控制;總而言之,所有UIVIew有關的對象。在你的程序里,你的AlbumVIew類就代表著View。
Controller: Controller 是一個“調解人”,協調所有的工作。它從model里面得到數據,然后在view里面展示它們,監聽事件并且如果需要會控制數據。你能猜到哪個類是你的controller么?沒錯,是ViewController.
在你的程序中執行一個好的設計模式意味著每個對象都可以被分到其中的一個組里。
通過Controller的View 和Model之間的交流可由下圖表示:

如果數據有變化Model會通知Controller,然后Controller會在View中更新顯示數據。View可以通知Controller用戶的行為,然后Controller會根據需要更新Model或者得到需要的數據。
你可能會疑惑為什么你不能直接拋棄Controll二,然后在一個類里面運行View和Model,這樣看起來會更簡單。這都因為要分離代碼以及重復利用性。理想中,View應該完全脫離Model。如果View完全不依賴一個特定的Model,那么它可以在另一個model中再次被利用來展示其他的數據。
舉例說,如果以后你想在你的庫中添加電影或者書籍,你依然可以用`AlbumView`來展示你的電影和書籍。更多的是,如果你想新創建一個有關專輯的項目,你還可以重復利用`Album`類,因為它并不依賴于任何View。這就是MVC的厲害之處!
## 怎么用MVC模式
首先,你需要確保你的項目中的每一個類必須是Controller,Model或者View;不要在一個類里混雜了兩個角色的功能。到現在你建立了`Album`類和`AlbumView`類,做得好!
其次,為了確保你遵守了這種工作的方法,你要創建三個組(group)來放你的代碼,每一個種類有一個組。
選擇`File\New\Group` (或者直接按`Command+Option+N`)并且給你的組命名為`Model`。重復這個過程來創建`View` 和 `Controller` 組。
現在把`Album.swift` 拖到Model組。把`AlbumView.swift`拖到View組,最后把`ViewController.swift` 拖到Controller組。
現在項目應該長這樣:

你的項目已經看起來很好了,所有文件都沒有到處亂放。很明顯你也可以有別的組合類,但是這個程序的核心都在這三個種類里。
現在你項目的組成部分已經很有序了,你需要從某個地方得到專輯的數據。你需要創建一個API類來管理數據,它將在你整個代碼中都被使用。這也意味著我們有機會來聊下下一個需要你學習的設計模式-單例。
##The Singleton Pattern
單例設計模式確保了制定類里只有一個實例并且有一個全局的訪問指向這個實例。當它第一次被用的時候,一般是以惰性加載的方式來創建單例。
注:蘋果用了很多這個方法。比如:NSUserDefaults.standardUserDefaults(), UIApplication.sharedApplication(), UIScreen.mainScreen(), NSFileManager.defaultManager() 他們都是返回一個單例的對象。
你很可能能想為什么需要關心一個類中是否不止一個實例。代碼和內存都很容易獲得啊,不是嗎?
在一些情況下,類里只有一個實例是比較符合邏輯的。比如說, 你的程序里只有一個實例,設備有一個主要的屏幕,所以你只需一個實例。或者處理全局配置類:實現一個線程安全的訪問指向到單個分享的資源,比如配置文件,比起很多類可能在同一時間改這個配置文件,會更簡單些。
## 單例模式怎么用
看下下圖:

上面的圖顯示了一個Logger類有一個屬性(一個單例)和兩個方法:`sharedInstance` 和 `init`。
當用戶第一次請求sharedInstance,單例屬性還沒有被初始化,所以你需要先創建一個類的實例,然后返回它的參考(reference)。
下一次你調用`sharedInstance`,`instance`(實例)就將立即返回不再需要其他的初始化。這個邏輯確保了每次只有一個實例存在。
你將通過創建一個實例類來管理全部數據來實現這個模式。
你講發現,在這個項目中有一個組叫`API`;這就是你將把所有給你程序提供服務的類放置的地方。右鍵這個組然后選擇`New File`來在這個組里創建一個新的文件。選`iOS > Cocoa Touch Class` 然后點擊`Next`。將這個類的名字設置為`LibraryAPI`, 子類為 `NSObject`。最后選擇 `Swift`作為編程語言,點擊`Next` 然后點 `Create`。
現在去`LibraryAPI.swift` 然后把這段代碼添加到類定義里。
//1 class var sharedInstance: LibraryAPI { //2 struct Singleton { //3 static let instance = LibraryAPI() } //4 return Singleton.instance }
這個很短的方法里干了很多事
它首先創建了一個類變量為計算類型屬性。這個類變量,就是OC里的類方法,是一個不需要實例化`LibarayAPI`類就可以調用的東西。想要更多關于類型屬性的信息,請去參考Apple Swift關于屬性的文檔。
在這個類變量里嵌入一個結構稱為`Singleton`
`Singleton`包含了一個叫instance的靜態常量。把一個屬性聲明為`static`(靜態)意味著這個屬性只出現一次。同時也要注意靜態屬性在Swift中為絕對惰性,當它被創造以后,它不會再一次被創造。這也是單例設計模式必須的,當它被初始化過后,這個初始化方法永遠不會再被調用。
返回計算類型屬性
Note: To learn more about different ways to create a singleton in Swift refer to this:Github page
注意`: 想要了解在swift中更多創建一個單例的方法,請去[GitHub網頁](https://github.com/hpique/SwiftSingleton)
你現在又了一個單例對象作為管理專輯的開始。讓我更進一步然后創建一個類來處理你的庫里數據的永久性。
現在在`API`組里創建一個新文件。選`iOS > Cocoa Touch class`然后點擊`Next`。把這個類的名字改成PersistencyManager,然后把它弄成`NSObject`的子類。
打開`PersistencyManager.swift`然后將下面的代碼放到大括號里。
private var albums =Album
現在你聲明了一個私有屬性來放置專輯的數據。這個數組是可變的,你可以很方便的添加或者刪除專輯。
現在講下面的初始化方法添加到類:
override init() { //Dummy list of albums //事先寫死的專輯列表 let album1 = Album(title: "Best of Bowie", artist: "David Bowie", genre: "Pop", coverUrl: "http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png", year: "1992")
let album2 = Album(title: "It's My Life", artist: "No Doubt", genre: "Pop", coverUrl: "http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png", year: "2003")
let album3 = Album(title: "Nothing Like The Sun", artist: "Sting", genre: "Pop", coverUrl: "http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png", year: "1999")
let album4 = Album(title: "Staring at the Sun", artist: "U2", genre: "Pop", coverUrl: "http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png", year: "2000")
let album5 = Album(title: "American Pie", artist: "Madonna", genre: "Pop", coverUrl: "http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png", year: "2000")
albums = [album1, album2, album3, album4, album5] }
在這個初始化方法里,你往數組里添加了五個樣本專輯。如果你不喜歡上面的專輯,你也可以自己換成你喜歡的音樂:]
現在把下面的方法添加到類:
func getAlbums() -> [Album] { return albums }
func addAlbum(album: Album, index: Int) { if (albums.count >= index) { albums.insert(album, atIndex: index) } else { albums.append(album) } }
func deleteAlbumAtIndex(index: Int) { albums.removeAtIndex(index) }
.
這些方法讓你可以得到,添加以及刪除專輯。
運行你的項目來確保所有東西都能正常編譯。
到現在這個時候,你可以會想什么時候為什么`PersistencyManager`會介入因為它并不是一個單例。你將在下一個章節中看到`LibraryAPI` 與 `PersistencyManager`之間的關系,下一個章節是關于外觀模式的。
#外觀設計模式

外觀設計模式給復雜的子系統提供一個借口。你只會有一個整合的API暴露給你,而不是一堆類和它們各自的API。
下面這個圖解釋了這個概念:

API的用戶并不完全知道API有多復雜。這個模式是當要用到大量類時,特別是那些非常復雜難懂難用的,非常理想好用的。
外觀模式將那些從接口處用系統的代碼和你要隱藏的執行類的代碼分離開;它并且減少你子系統內部對外面代碼的依賴性。如果外觀模式下的代碼很有可能變,那么這個就非常有用,因為外觀類可以保持同樣的CPI當其它東西在幕后變化。
舉個例子,如果你有一天想要換你的后端服務,你并不需要改變你的代碼因為它的API并不會改變。
##怎么用外觀模式
現在你有`PersistencyManager `來本地儲存專輯數據,和`HTTPClient` 來處理遠端通訊。在你的項目中,其它的類不應該知道這個邏輯,因為它們會被`LibraryAPI`隱藏在外觀后。
為了實現這個模式,只有`LibraryAPI`擁有`PersistencyManager` 和 `HTTPClient`。并且,`LibraryAPI`會暴露給一個簡單的API使它可以獲得這些服務。
這個模式長這樣:

`LibraryAPI`會暴露給其他的代碼,但是會對app中其它部分隱藏`HTTPClient` 和`PersistencyManager`的復雜性。
打開 `LibraryAPI.swift`然后添加下面這些常量屬性到類里:
private let persistencyManager: PersistencyManager private let httpClient: HTTPClient private let isOnline: Bool
`isOnline` 決定了當專輯列表有任何改變時,比如添加或刪除專輯,服務器是否要更新。
你現在需要通過`init`初始化這些變量。將初始化方法添加到類:
override init() { persistencyManager = PersistencyManager() httpClient = HTTPClient() isOnline = false
super.init() }
HTTP Client 并不真正與真實的服務器工作而且只在這里需要聲明外觀模式的運用,所以`isOnline`永遠是否(false)。
然后,將下面三個方法添加到 `LibraryAPI.swift`:
func getAlbums() -> [Album] { return persistencyManager.getAlbums() }
func addAlbum(album: Album, index: Int) { persistencyManager.addAlbum(album, index: index) if isOnline { httpClient.postRequest("/api/addAlbum", body: album.description) } }
func deleteAlbum(index: Int) { persistencyManager.deleteAlbumAtIndex(index) if isOnline { httpClient.postRequest("/api/deleteAlbum", body: "(index)") } }
看下`addAlbum(_:index:)`。這個類首先本地更新數據,如果有網絡連接,它再更新遠程服務器。這才是外觀模式真正的厲害之處;當你的系統之外的什么類添加了一個專輯,它并會不知道-也不需要知道-其底下的復雜性。
注:當給你的子系統的類設計一個外觀模式時,記住什么都不能阻止客戶直接進入那些被“隱藏”的類。不要吝嗇地加上防守的代碼,也不要假設所有客戶都會像外觀模式那樣用你的類。
編譯運行你的app。你應該會看到兩個空的界面和一個工具欄。上面的界面會用來展示你的專輯封面,下面那個界面將用來展示專輯相關信息的表格。

你現在需要一些可以展示專輯數據到屏幕上的東西-是一個好機會用你下一個設計模式了:修飾模式。
#修飾設計模式
修飾模式動態添加行為和責任到一個對象而不改變它的代碼。這是一個用來創建子類的另一種方法:當你通過把一個類包裝到另一個對象來更改類的時候。在swift中有兩個非常常見的方法來實施這個模式:`Extensions` 和 `Delegation`
##擴建
添加一個擴建是非常有用的技巧,它使你可以給現有類、結構、枚舉類型添加新功能而不用讓它們成為子類。這個的絕妙之處在于你可以擴展你的那些你沒有權限的代碼,并且增加它們的功能性。這個意味著你可以Cocoa類里,比如UIView和UIImage里加入你自己的方法。
比如說,在編譯時添加的新方法可以想擴展類里的普通方法一樣被執行。這個與典型的修飾模式還是有一點點的區別,因為一個擴建不不擁有它擴建的那個類的實例。
## 怎么用擴建
想象一下當你有一個專輯對象,然后你想在表單里展示這個。

專輯標題從哪來呢?`Album`是一個在model里的對象,所以它不關心你怎么展示數據。你只需要有一些外部的代碼來添加功能到Album類而不用直接更改這個類。
你首先創建一個擴展Album類的擴建,它會定義一個新方法,這個方法會返回一個方便與`UITableView`用的數據結構。
這個數據結構看上去像下面這樣:

想要給Album添加一個擴展,去到`File\New\File… `然后選擇 `iOS > Source > Swift File` 模版, 然后點擊 `Next`. 鍵入 `AlbumExtensions` 然后點擊 `Create`
Go to `AlbumExtensions.swift` and add the following extension:
去`AlbumExtensions.swift`然后添加下面的擴展:
extension Album { func ae_tableRepresentation() -> (titles:[String], values:[String]) { return (["Artist", "Album", "Genre", "Year"], [artist, title, genre, year]) } }
注意在這個方法名字的開頭有一個`ae_`,這是`AlbumExtension`的縮寫。這樣的規則會避免在原始執行中的方法有沖突(包括那些你可以不知道的私有的方法)。
注意:類當然可以是父類的方法無效,不過擴展的話是不可以的。擴展里方法和屬性是不能與原先的類里的方法和屬性有同樣的名字。
想一下這個模式有多么的厲害:
* 你直接用`Album`里的屬性。
* 你已經添加到了`Album`類里了,但是你還沒有讓它變為子類。如果你想要讓它變為`Album` 子類,你依舊可以這么做。
* 這步簡單的添加可以讓你返回一個類似UITableView的專輯描述,而不用更改Album里的代碼。
##代理
.
另外一個修飾模式,代理,是一個對象代理或者與另一個對象合作的機制。比如說,當你用`UITableView`時,其中一個你必須用到的方法是`tableView(_:numberOfRowsInSection:)`。
你不能指望`UITableView`來指導每個部分里有幾行,因為這具體到了應用。所以,`UITableView`的代理就有了計算行數的任務。這使得`UITableView`類獨立于它要展示的數據。
這里是一個大概的解釋當你創建一個`UITableView`時發生了什么。

這個`UITableView`對象要展示一個表單。不過,最終它會需要一些它沒有的信息。然后,它轉向他的代理們然后發出需要其他信息的請求。在Objective-C實施代理模式里,一個類可以通過協議聲明非強制的和必須的方法。在這個教程的后面一點你會學到協議的。
只是加個子類然后覆蓋必須的方法看上去很簡單一下,不過要注意,對一個子類只能有一個父類,如果你想要這個對象成為2個及以上對象的代理,你將不能用子類的方法。
注意:這是一個非常重要的模式。Apple在大多UIKit類里用這個方法:UITableView, UITextView, UITextField, UIWebView, UIAlert, UIActionSheet, UICollectionView, UIPickerView, UIGestureRecognizer, UIScrollView. 這個單子持續更長。
##怎么使用代理模式
打開`ViewController.swift`然后給它加上一些私有的屬性。
private var allAlbums =Albumprivate var currentAlbumData : (titles:[String], values:[String])? private var currentAlbumIndex = 0
接下來,用下面的代碼替換`viewDidLoad`。
override func viewDidLoad() { super.viewDidLoad() //1 self.navigationController?.navigationBar.translucent = false currentAlbumIndex = 0
//2 allAlbums = LibraryAPI.sharedInstance.getAlbums()
// 3 // the uitableview that presents the album data dataTable.delegate = self dataTable.dataSource = self dataTable.backgroundView = nil view.addSubview(dataTable!)
}
現在讓我們分析一下上面的代碼:
1. 關掉導航欄的透明度。
2. 從API那里得到所有專輯的列表。記住,我們的計劃使用LibraryAPI的外觀模式而不是直接用 `PersistencyManager`
3. 現在這步就是要建立`UITableView`了。你要聲明view controller是`UITableView`代理/數據的源頭;所以,所有`UITableView`需要的信息都會由view controller提供。注意你其實可以在storyboard設置代理和數據源,如果你的表單是在那里創建的。
Now, add the following method to `ViewController.swift`:
現在,將下面的方法添加到`ViewController.swift`:
func showDataForAlbum(albumIndex: Int) { // defensive code: make sure the requested index is lower than the amount of albums if (albumIndex < allAlbums.count && albumIndex > -1) { //fetch the album let album = allAlbums[albumIndex] // save the albums data to present it later in the tableview currentAlbumData = album.ae_tableRepresentation() } else { currentAlbumData = nil } // we have the data we need, let's refresh our tableview dataTable!.reloadData() }
`showDataForAlbum()`從專輯數組里得到需要的專輯數據。當你想要展現新的數據時,你只需要調用`reloadData`。這個會使`UITableView` 問他的代理,比如這個表單里需要幾個部分,每個部分里有多少行,每個單格應該長什么樣之類的問題。
把下面這行添加到`viewDidLoad`的最后。
self.showDataForAlbum(currentAlbumIndex)
這個會在打開app的時候載入當前的專輯。而且因為`currentAlbumIndex`之前被設置成0,這會展示列表里的第一個專輯
現在是執行數據源協議的時候了!你先添加由類執行的在類聲明那行的協議列表。或者,為了使東西看起來干凈點你可以把他們添加成擴展,你應該已經對擴展很熟悉了。
將下面的擴展添加到文件的尾部。你要確保把這些添加在類定義的大括號之前!
extension ViewController: UITableViewDataSource { }
extension ViewController: UITableViewDelegate { }
這就是怎么使你的代理湊手協議 - 把它想象成這是代理做出的滿足方法合同的承諾。這樣,你表明了`ViewController`會遵守 `UITableViewDataSource` 和 `UITableViewDelegate`的下移。這樣` UITableView`可以百分百確保這些需要的方法由代理執行了。
將下面的代碼添加到`UITableViewDataSource` 擴展
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if let albumData = currentAlbumData { return albumData.titles.count } else { return 0 } }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell if let albumData = currentAlbumData { cell.textLabel!.text = albumData.titles[indexPath.row] cell.detailTextLabel!.text = albumData.values[indexPath.row] } return cell }
`tableView(_:numberOfRowsInSection:)` 返回表單里的行數,而且這與數據結構里的標題數量一致。
`tableView(_:cellForRowAtIndexPath:) `創建并且返回了單元的標題和值。
注:你實際需要添加方法到主類的聲明里或者到擴展:編譯器不會在乎數據源方法到底是不是在UITableViewDataSource擴展里。當然對于別人讀代碼來說,這樣的結構相當幫助可讀性。
編譯運行你的程序。你的app應該啟動而且展現下面的屏幕:

表單數據源成功!
##現在能去哪里?
現在東西看上去不錯!你的MVC模式在它該在的地方,而且你也看見過了單例,外觀,修飾模式運作時的樣子。你也可以看他們在由Apple寫的Cocoa里樣子,也能看這些模式如何能被運用到妮子的代碼。
這里是到現在為止的[項目文件](http://cdn1.raywenderlich.com/wp-content/uploads/2014/12/BlueLibrarySwift-Part11.zip)
還有很多沒學過的:比如適配器,觀察者和備忘錄模式將在[本教程的第二部分](http://www.raywenderlich.com/90773/introducing-ios-design-patterns-in-swift-part-2)提到。而且這還不是全部,我們將會有一個復查的教程,里面有更多的設計模式來讓你寫一個簡單的iOS游戲。
如果你還有其他的問題或者只是想提到你最喜歡的設計模式,加入到下面的討論!