做者:gregg mojica,原文連接,原文日期:2015-11-30
譯者:小鐵匠Linus;校對:Cee;定稿:[](undefined)javascript
2014 年 6 月 Apple 發佈 Swift 以來,如何在 Swift 中進行網絡編程一直成爲程序猿們關注的焦點。甚至,Chris Lattner,Swift 的做者之一,也發推說過,在 Swift 中解析 JSON 還有很長的路要走。所以,許多人開始尋求替代方案。儘管,在 Swift 中也有處理 JSON 解析的內建類,可是對開發者來講並非很友好。幸運的是,Alamofire 出現了。Alamofire 是一個能夠幫助咱們解析 JSON 的強有力網絡庫,它由 Objective-C 中同類網絡庫 AFNetworking 的做者編寫。html
在這個又臭又長、近乎 3500 多詞(譯者注:in English)的教程中,咱們將探討一系列普遍的網絡基本話題,並創建一個假日待辦應用。java
同時,你會從本教程中學到:如何使用和解析 JSON,如何自定義服務器端,如何使用 Heroku 和 MongoLab 等工具,HTTP 的工做原理(包括 GET,POST 和 DELETE 請求),如何使用 git 和終端(terminal)以及如何使用 Cocoapods。若是你以爲上面提到的內容太多了,那就對了,拿一杯咖啡,就讓咱們開始吧。node
哦,AppCoda 的全部做者祝你們節日開心!?ios
注意:本教程是一個進階教程,涵蓋了不少東西。並且,我假設你已經對 iOS 和 Swift 有了很堅實的瞭解。文章中諸如 tableviews, autolayout,delegate 等話題都不會深刻的解釋原理。你若是記不清這些內容,能夠先去學習咱們推出的優秀課程,而後再回來看本教程。git
爲了實現本教程要實現的功能,我已用 Node.js 寫了一個服務器後端。這裏須要給那些對它不熟悉的人解釋一下,Node.js 是一個基於 Javascript、運行在 Google Chrome 的 V8 引擎中的運行時環境。長話短說,總之它是一個特別可靠,速度特別快,特別厲害的東西,哈哈。github
爲了搞定這個後端,我同時也使用了 Restify 和 MongoDB。MongoDB 是在 Web 開發人員中很流行的一個 no-SQL 數據庫。咱們可使用 MongoDB 存儲全部咱們相關的數據。mongodb
當我剛開始使用 Node 的時候,我不知道這些東西都是怎麼運行的,其餘我所瀏覽的一些博客也從沒有解釋 Node 究竟是怎麼工做的。所以,儘管這是個 iOS 的博客,但我仍是要介紹一下 Javascript 和 Node 服務器的工做原理。數據庫
我搜遍了網絡,都沒有一個詳細的教程引導你建立一個 API 與 iOS 應用程序交互的步驟,從如今開始就有了。express
像我以前提到的同樣,Node.js 是一個很強大的服務器端開發技術,它創建在 Chrome 的運行時環境上。所以,它是高度異步的和非阻塞的(若是你不知道我說的是什麼意思,其實很簡單,大概就是:使用主線程或者應用的主要部分不會被阻塞)。多線程是一種能夠防止延遲且能提升項目效率的編程技術。你把應用想象成一條高速公路,若是隻有一條通道,卻有 20 輛車要經過,那麼他們就頗有可能會堵車。若是一條高速公路有三條都有出入口的通道,那麼堵車的機會就很小。多線程就能夠這樣來理解。在一個多線程的環境裏,代碼執行在不一樣的線程就能夠避免應用阻塞,從而防止程序奔潰。
Node 是由 Joyent 開發並維持的,Joyent 是一家位於舊金山的雲計算公司。
若是你仍然不清楚全部這些是怎麼運行的,想一想後端具體幹了些什麼吧。下面列出來一些:
後端是一個爲你傳送 API 的地方(咱們如今正在爲當前的應用構建 API,就和網絡上其餘的 API 同樣,其中包括了咱們在以前的 tvOS 教程中使用的 forecast.io API)。
MongoDB 提供了一個保存全部數據的地方。當你想要 POST 一條新的消息,咱們須要有個地方能夠存儲這條消息。在本教程中,咱們將把這些數據存儲到 MongoDB 數據庫中。
建立一個功能完整的 REST API,它遵循 REST 協議。
咱們的 MongoDB 放在 MongoLab 的主機上,Node 服務器放在 Heroku 上。Heroku 由 Salesforce 提供支持,能夠做爲 Node,Rails,Python等應用的主機服務商。MongoLab 也是一家能夠當 MongoDB 主機的服務商。
在咱們開始寫代碼以前,你應該瞭解 HTTP 請求以及如何在咱們的應用裏使用。
GET 請求 - GET 請求會查詢咱們的數據庫,而後獲取內容。GET 請求能夠被限制,使得其只能獲取一個、多個或所有的內容。事實上,每次你訪問 google.com 或瀏覽你的 Facebook/Twitter 主頁,你都會發起 GET 請求(可能你以前都不知道這個東西)!
POST 請求 - POST 請求會發送數據到服務器,而後保存這個數據。舉個例子,當你在 Facebook 或 Twitter 上寫好文字,而後按 Post/Tweet 按鈕的時候,你就發起了 POST 請求。
UPDATE 請求 - UPDATE 請求可讓你修改已經存在的內容。當你編輯一條 Facebook 消息時,其實使用到了 UPDATE 請求。
DELETE 請求 - DELETE 請求會刪除對應的內容。當你按了刪除按鈕刪除 Facebook 或 Twitter 消息的時候,實際上是調用了 DELETE 請求。
以上這四個請求類型是基於 REST 協議的。Internet 能運行就是由這些請求組成的。你可能也據說過 CRUD 這個縮寫詞,CRUD 是由 Create,Read,Update 和 Delete 的首字母組成的。很顯然,這些單詞就和 POST,GET,UPDATE 和 DELETE 是一一對應的。
帥氣!如今咱們已經對 HTTP 協議有必定的理解了,咱們能夠進入到此次教程的核心部分了。
在咱們使用 MongoLab 或 Heroku 以前,咱們應該要確保 Node.js 能正常使用。
打開 Node.js官網,根據簡單的引導下載 Node 到你的電腦上。
而後,到 npm 官網下載 npm。
爲了正確配置咱們的後端,咱們須要分別在 Heroku 和 MongoLab 上註冊賬號。咱們先從 MongoLab 開始吧,去 MongoLab 官網註冊賬號。
確保選擇的是 single-node(免費),填上你數據庫的名字。我這邊取名爲 alamofire-db(以 db 爲後綴表示是一個數據庫,這是比較廣泛的命名規範)。
接下來,登陸你的數據庫,定位好 MongoDB 數據庫的 URI。
立刻就讓你添加一個新的數據庫賬號,輸入用戶名和密碼。不要忘記密碼。
如今返回到你設置 URI 的頁面,修改爲新的地址。好比:
mongodb://<dbuser>:<dbpassword>@ds057954.mongolab.com:57954/alamofire-db
替換成:
mongodb://gregg:test@ds057954.mongolab.com:57954/alamofire-db
MongoLab 搞定!
如今去 Heroku.com,免費註冊後,打開 heroku toolbelt 頁面。
跟隨指南,成功安裝後,打開終端並登陸 heroku。若是你以前從未使用過終端,不用擔憂。本教程會屢次使用終端,這樣你最終就會對終端的使用有一個清晰的認識。
一旦你在終端上登陸 heroku,可使用 cd 命令(cd 表明改變目錄)進到對應目錄,將以前從 dropbox 下載的工程文件夾移動進去。
按下回車鍵就能夠執行這行命令了。乾的不錯,如今咱們能夠用 git 提交(Push)東西到 heroku 了。
在終端中鍵入如下命令:
bash git init git add . git commit -m "First Commit"
這三行命令,初始化了一個倉庫(repository,簡寫爲 repo),並添加了當前目錄下的全部文件到這個倉庫,最終提交併保存。
git 是一款很流行的版本控制軟件。
如今你能夠看終端裏應該和下圖的內容差很少:
由於你以前已經成功安裝了 heroku toolbelt,因此你如今能夠在終端裏鍵入 heroku login,並輸入賬號密碼。敲回車後繼續,若是賬號密碼沒問題的話,你的 Email 會以藍綠色高亮顯示。
如今,鍵入 heroku create 來建立一個新的 heroku 應用。Heroku 會建立一個新的帶有域名的應用給你。好比,個人就是 https://whispering-plains-1537.herokuapp.com/。
如今,鍵入 git push heroku master 來把你新建的應用發送到 heroku。
若是一切順利的話,會顯示以下圖(其中的某一些設置可能會不一樣)。
讓咱們從下載示例工程開始,連接在這裏。打開你最喜歡的文本編輯器(我這邊用的是 Sublime Text 2;能夠在這裏下載免費版,若是你支持的話也能夠購買),而後繼續。
Javascript 在很大程度上是和 Swift 很類似的。咱們以後會使用 express 和 mongoose 兩個著名 node 包。請確保你已經在系統上安裝 npm 和 node 包管理器。
Express 是 Node.js 中的一個「快速、強大而又輕量級」的網絡框架,它能夠輕鬆解決路由(Route)問題。你問什麼是路由?路由就是你與網絡交互的方式。每次你打開 google.com 的時候,其實你訪問的是根主頁,即 google.com/。假如你訪問 google.com/hello,那就是另一個路由了。咱們接下來將要定義一個能訪問咱們數據庫的路由。
你能夠從 expressjs.org 官網上學習更多關於 express 的知識。
下面是示例代碼:
javascript var express = require('express'); // 1 var app = express(); // 2 // 當一個 GET 請求訪問主頁的時候,會返回 hello world app.get('/', function(req, res) { // 3 res.send('hello world'); // 4 });
第一行代碼設置了一個叫 express 的變量。第二行代碼,把 express 初始化後賦值給一個叫 app 的變量。在第三行代碼,app 這個變量表明瞭 express 環境,調用它的 get() 方法(形式相似 Swift)。當一個用戶訪問 / 根主頁的時候,就會顯示「hello world」。這是 express 做爲路由的一個例子。若是須要更多信息,能夠訪問 express 官網查看。
如今,咱們已經配置好了 mongo 數據庫的環境,接下來讓咱們來使用 cURL 請求測試一下功能。cURL 是一款命令行程序,它能夠發送 HTTP 請求。咱們將會先使用 cURL 作一下實驗,而後再遷移到 Alamofire 去。
打開你的文本編輯器(再次順便說一下,我用的是 Sublime),同時打開 app.js 文件。正如你看到的,應用被分割成了一個 model 和路由文件(就是你剛打開的 app.js 文件)。model 文件能夠創建模式(schema)或數據庫結構。讓咱們先來簡單看看這個文件吧。
javascript var mongoose = require('mongoose'), Schema = mongoose.Schema; var TodoSchema = new Schema( { name: String }); mongoose.model('employees', TodoSchema);
咱們可使用 mongoose,它是一個用在應用與 mongo 之間做爲接口的 npm 包。我起初在構建一個僱工跟蹤應用,並把 model 命名爲 employee,可是可能會隨時修改這個 model。我保留着它,是由於這個教程的接下來部分可能會用到。
Mongoose 能很方便的提供與 mongoLab 的 heroku node 應用鏈接並提供相應的接口。這的確很是方便。
路由文件裏存的是咱們將會輸出到 app.js 文件的內容。不用太擔憂這個輸出——它是 node 中一個比較先進的特性,也超出了本教程的範圍。
注意第 26 行的 newTodo。正如你可能猜到的,這行代碼建立了一個新的 todo。
javascript var emp = new Todo(req.body); emp.save(function(err){ if (err) { res.send('Error occurred'); return console.log(err); } res.send(emp); });
咱們把 Todo 對象(在第四行定義了一個與 mongoose 鏈接的對象)賦值給一個叫 emp 的變量,並設置 req.body(req 表明請求,它會發送給咱們數據,同時,res 表明回覆,它會返回咱們的要返回的東西)。
隨意瀏覽一下文件中剩下的方法。
如今回到 app.js 文件,這裏是整個應用的主要部分。接下來列出來一些這個文件裏的重點部分(譯者注:對照下圖看):
第 13 行代碼創建 express 應用
第 15 - 22 行代碼配置該應用
第 33 行代碼使用 mongoose 將應用鏈接到 mongoLab 數據庫
第 35 行代碼創建鏈接
第 41 - 45 行代碼創建應用的路由文件並鏈接到 /routes/todo.js 文件
第 48 行代碼建立服務器
以上這些,能讓你瞭解到一些 Javascript 應用的基本運做知識。可是,畢竟這篇教程不是主講 Javascript 的,我不會繼續深究。固然,我仍是鼓勵大家去研究一下 express 和 mongoose。
在咱們的 node 應用開啓狀態下,咱們能夠執行一些 cURL 請求來作測試。一旦咱們作完測試,就能夠遷移到 Alamofire 上去了。
在終端裏執行下面的代碼(記得將 url 修改爲你本身對應的 heroku url)。
bash curl -i -H "Accept: application/json" "https://rocky-meadow-1164.herokuapp.com/todo"
命令行中的 -i 和 -H 參數,表示咱們將要接收什麼東西。咱們會接收 JSON 並將 JSON url 追加到請求的末尾。
你應該能看到有數據返回了。和下圖差很少。
正如你看到的,返回的數據就是咱們想要獲得的。若是你已經將 url 替換成你本身的,你可能什麼也看不到,由於你的 mongodb 裏如今還沒數據。
加入你想要加一些數據到數據庫裏,你須要的就是下面的 POST 命令。
bash curl -H "Content-Type: application/json" -X POST -d '{"name":"Buy Presents"}' https://rocky-meadow-1164.herokuapp.com/todo
而後,你使用以前講過的 GET 請求,就能夠看到你剛纔添加的「Buy Presents」的內容了。
bash curl -X DELETE 'https://rocky-meadow-1164.herokuapp.com/todo/5657901fee93910900cc54ed'
很棒!接下去咱們不會講 PUT 請求,由於在咱們這個應用裏暫時還用不上。可是它和其餘的請求使用起來是差很少的。
讓咱們重新建一個名叫 TodoApp 的 Xcode 工程開始吧。由於假期就要到來,咱們應該有一種方式來跟蹤這件事情。幸運的是,咱們有咱們的 node 應用能夠幫忙。
雖然你能夠手動安裝 Alamofire(經過拖拽源文件到對應工程的方法),可是咱們選擇使用 Cocoapods。Cocoapods 是一款爲 iOS 工程提供依賴管理的工具。在使用 Cocoapods 的時候,開發者能夠輕鬆的添加框架或第三方類庫。若是你以前沒有使用 Cocoapods,強烈推薦你去使用。
接下來,在終端裏運行如下命令能夠確保你在接下來的步驟後成功安裝 Cocoapods。
bash $ gem install cocoapods
而後,經過 cd 命令進入你工程所在的目錄,鍵入如下命令。
bash vim Podfile
Vim 是一款系統自帶的命令行編輯器,與 Sublime Text 或 TextMate 相似。咱們如今要新建一個 Podfile 的文件,Cocoapods 每次都會去這個文件裏查詢是否須要更新工程的 pod(包括各類的依賴)。
在 Podfile 這個文件裏鍵入以下內容:
ruby source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' use_frameworks! pod 'Alamofire', '~> 3.0'
而後,按 ESC 鍵,並輸入 :wq
,再敲回車。其中,wq 表示保存並退出。
咱們如今已經成功建立 Podfile 而且保存了,爲了安裝 CocoaPods,在終端裏輸入如下命令:
bash pod install
敲了回車後,若是一切都設置好的話,大概會呈現下圖顯示的內容。
這時候,你能夠看到命令行裏要求你關閉當前打開的 Xcode 而且之後都用 .xcworkspace 爲後綴的文件來打開工程。
下面這個命令可以很是方便地打開當前目錄的 finder 界面。到此爲止咱們在 Terminal 中的操做就那麼多,看上去一天以內有那麼多就夠了!
bash open .
打開 ViewController.swift,讓咱們繼續吧。
在打開的 ViewController.swift 裏,輸入如下代碼來導入 Alamofire:
import Alamofire
在 viewDidLoad() 方法裏鍵入如下代碼來使用 Alamofire。
Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo") .responseJSON { response in // 1 print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { print("JSON: \(JSON)") } }
在第一行代碼中,咱們聲明瞭一個 GET 請求,而且傳入了一個咱們須要的 URL。運行當前的應用,看看返回的是什麼。若是一切都設置正確的話,你會看到返回的是 JSON 數據。
如今,打開 Main.storyboard,添加一個 tableview 到 view controller,並將視圖控制器嵌入到 navigation controller。你的 storyboard 如今看起來應該跟個人同樣,以下圖(值得注意的是,如今返回的 JSON 數據還只是顯示在控制檯上,咱們要將其顯示出來。)。
將如下代碼複製並粘帖到你的 Viewcontroller.swift 文件裏。
import UIKit import Alamofire class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! var jsonArray:NSMutableArray? var newArray: Array<String> = [] override func viewDidLoad() { super.viewDidLoad() Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo") .responseJSON { response in print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { self.jsonArray = JSON as? NSMutableArray for item in self.jsonArray! { print(item["name"]!) let string = item["name"]! print("String is \(string!)") self.newArray.append(string! as! String) } print("New array is \(self.newArray)") self.tableView.reloadData() } } // Do any additional setup after loading the view, typically from a nib. } }
我初始化了兩個數組 jsonArray 和 newArray,用 for 循環遍歷了返回數據的那個 jsonArray,將其中的每一個數據保存到 newArray 中。
我使用 POST cURL 請求在數據庫裏多添加了一些數據。用法相似,再也不贅述。
你能夠試試下面代碼演示的 GET 請求的極致精簡寫法。
Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo").responseJSON { response in debugPrint(response) }
接下來,在文件頂部的 UIViewController 定義後面添加 UITableViewDelegate 和 UITableViewDataSource。而且,在 viewDidLoad() 方法裏鍵入以下代碼:
self.tableView.dataSource = self self.tableView.delegate = self
最後,添加 UITableView 的 delegate 方法。
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.newArray.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell cell.textLabel?.text = self.newArray[indexPath.row] return cell } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. }
正如你看到的,咱們的 tableview 已經成功顯示數據了。
如今,讓咱們來添加一個按鈕,用來添加數據到列表中。首先,先在 storyboard 裏添加一個叫 AddViewController 的類,並用 segue 的方式鏈接起來。你的 storyboard 應該和下圖差很少。
在你的 AddViewController.swift 文件裏,爲 textfield 創建一個 IBOutlet(命名爲 textView)和爲 Save 按鈕創建一個 IBAction。在 Save 按鈕代碼下面鍵入以下代碼:
Alamofire.request(.POST, "https://rocky-meadow-1164.herokuapp.com/todo", parameters: ["name": self.textView.text!]) self.navigationController!.popViewControllerAnimated(true)
正如你看到的,Alamofire 大大簡化了發送 POST 請求的過程。
接下來,咱們來對 ViewController.swift 文件進行重構,確保咱們在保存數據後能及時更新列表。刪除 viewDidLoad() 方法裏 GET Alamofire 的代碼,用如下的 downloadAndUpdate 方法代替。
func downloadAndUpdate() { Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo") .responseJSON { response in print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { self.jsonArray = JSON as? NSMutableArray for item in self.jsonArray! { print(item["name"]!) let string = item["name"]! print("String is \(string!)") self.newArray.append(string! as! String) } print("New array is \(self.newArray)") self.tableView.reloadData() } } }
如今,在 viewWillAppear() 方法裏調用這個方法,以下。
override func viewWillAppear(animated: Bool) { self.downloadAndUpdate() }
若是你再次編譯並運行這個應用,就會發現每次添加新的 todo 後都會從新加載。可是,這是爲何呢?
這就關係到 view controller 的生命週期,這裏我就簡短討論一下。viewDidLoad() 會在 view 初始化後而且全部控件都結束加載後被調用。問題就出在,當你從已經加載的 ViewController 上加載另一個 view(好比 AppViewController)時,viewDidLoad 方法不會被調用(以前已經初始化過)。viewWillAppear 方法會在每次 view 在屏幕上顯示時調用。由於咱們須要在再次顯示 ViewController.swift 時候顯示,因此這個方法恰好可用。
如今在剛纔的 newArray 下面添加一個 IDArray。
var IDArray: Array<String> = []
接下來,更新 downloadAndUpdate 方法的相應部分,代碼以下。
self.newArray.removeAll() // NEW self.IDArray.removeAll() // NEW Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo") .responseJSON { response in print(response.request) // original URL request print(response.response) // URL response print(response.data) // server data print(response.result) // result of response serialization if let JSON = response.result.value { self.jsonArray = JSON as? NSMutableArray for item in self.jsonArray! { print(item["name"]!) let string = item["name"]! let ID = item["_id"]! // NEW self.newArray.append(string! as! String) self.IDArray.append(ID! as! String) // NEW } print("New array is \(self.newArray)") self.tableView.reloadData() } }
兩行帶有 NEW 註釋的代碼是新添加的。從代碼的本質上來講,咱們在循環中得到對應的 ID 並保存到數組 IDArray 中。一樣,咱們也須要將不須要的數據從列表中刪除並重置。
添加 commitEditingStyle 方法,以調用 DELETE 請求來刪除對應的不須要數據。
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { if editingStyle == .Delete { print("ID is \(self.IDArray[indexPath.row])") Alamofire.request(.DELETE, "https://rocky-meadow-1164.herokuapp.com/todo/\(self.IDArray[indexPath.row])") self.downloadAndUpdate() } else if editingStyle == .Insert { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } }
正如你看到的,以上代碼遵循了咱們應用的 API,即經過傳入 /todo/ID 來調用 DELETE 請求刪除對應的數據。
同時,咱們用比較簡單的 Alamofire 方法來調用 DELETE 請求並刪除了對應的數據。
至此,你如今已經擁有了一個功能完備的 todo 應用了。所以,讓咱們來總結一下本次教程吧。
本教程探索了不少東西。從 Javascript 的 node 到 express,從 MongoDB 到 cURL,從終端到 Cocoapods,以及最後的 Alamofire,咱們深刻了解了 REST API 的建立過程和網絡的工做流程。你經過本次教程應該已經堅實的掌握瞭如下內容:
構建 API
部署 API
寫客戶端應用
使用 HTTP 請求
使用 Cocoapods
使用 cURL
使用 node 和 MongoDB/Express
使用 Express 作路由
使用 Alamofire
這真是一個大教程,我感謝你堅持和我走到這裏。全部的源代碼能夠在這裏下載,其中包含了 node 應用和 iOS 應用。
有任何問題和想法均可以在教程下面留言評論。下次見!
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。