做者: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。