通过Swift 3上的iOS中的API与服务器进行交互。第2部分

本文是文章在Swift 3上通过iOS中的API与服务器交互以及第1部分更新并在Swift 3上更新用Objective-C编写的旧文章的延续。

简要理论


GET请求


GET是最简单的HTTP请求方法,它是浏览器用来加载网页的方法。 它用于请求位于特定URL的内容。 内容可以是例如网页,图片或音频文件。 按照惯例,GET请求是只读的,并且根据W3C标准,不应在更改服务器端的操作中使用GET请求。 例如,我们将不使用GET请求发送表单或发送照片,因为这些操作需要在服务器端进行一些更改(在这种情况下,我们将使用POST)。

POST请求


POST将数据发送到URL进行进一步处理。 参数包含在请求正文中,格式与GET相同。 例如,如果我们要发布一个包含两个字段的表单,一个名称和一个年龄,那么我们将在请求正文中发送类似于name = Martin&age = 29的内容。

这种发送参数的方法已在网页中广泛使用。 最受欢迎的案例是表格。 当我们在网站上填写表格并单击“提交”时,最有可能的请求将是POST。

在我们的应用程序中,我们使用POST对笑话进行排名。 我们会将选票(或+1或-1)发送到远程服务器。

POST请求数据可以使用不同的格式进行结构化。 参数通常按照形式URL编码标准(按照W3C HTML标准)进行格式化。 这是默认格式,已在许多浏览器中广泛使用。 我们的方法接受Dictionary字典作为参数,但是我们无法通过HTTP连接发送Dictionary字典,因为它是内部Swift类型。 要通过HTTP连接转发,我们需要创建字典的可识别表示形式。 就像与外国人交流一样。 我们将消息翻译成通用语言,然后将其从通用语言翻译成其母语。 HTTP中的通用语言是W3C标准,我们的语言是Swift,接收方语言对我们来说是未知的。

W3C标准定义了规则,这些规则定义了每种情况下可识别的表示形式的含义。 在我们的例子中,我们需要在标准的形式URL编码部分之后显示参数(例如,param1 = var1和param2 = var2)。

网页浏览


使用编程语言工具,我们可以将请求发送到远程服务器。 这是浏览器在显示网页之前所做的。 区别仅在于答案的内容。 网页使用HTML标准进行格式化,该HTML标准定义了一组有关如何以图形方式定义各种标记标签的规则。 这些规则看起来很简单,但是要遵循W3C标准显示整个页面是一项艰巨的任务。 幸运的是,iOS具有内置的UIWebView组件,该组件使用著名的WebKit引擎并解释HTML / CSS / JavaScript并在UIView中显示整个网页。

在某些情况下,我们要控制导航流程。 例如,我们想知道何时加载某些内容或特定的URL。

或者,也许我们正在为儿童实施安全的浏览器,我们希望阻止用户加载符合某些条件(例如性别或毒品)的网页。 对于所有此类自定义类型,我们创建一个类的实例,该类将UIWebViewDelegate协议实现为UIWebView委托。 我们可以实现以下方法:

webView:shouldStartLoadWithRequest:navigationType: webViewDidStartLoad: webViewDidFinishLoad: webView:didFailLoadWithError: 

使用第一种方法,我们可以通过允许或阻止特定请求来控制导航的流程。 其他三种方法是信息事件(方法名称可以很好地说明事件)。

通过POST请求实现投票


我们将添加MainViewController.swiftHTTPCommunication.swift文件 ,并创建一个新的WebViewController.swift文件,在其中定义webView。

首先,创建一个新的WebViewController.swift文件并在其中定义WebViewController类:

WebViewController.swift

 import UIKit class WebViewController: UIViewController { } extension WebViewController { } 

现在,我们在负责所有HTTP操作的类中实现了发出POST请求的功能:HTTPCommunication类。 为此,请在HTTPCommunication.swift中添加一个新的postURL(_:params:complementHandler :)方法,该方法与先前的retrieveURL(_:completionHandler :)方法相似。

HTTPCommunication.swift

 class HTTPCommunication: NSObject { … func retrieveURL(_ url: URL, completionHandler: @escaping ((Data) -> Void)) { … } // TODO:    } // end of class HTTPCommunication 

方法代码

 func postURL(_ url: URL, params: [String: Int], completionHandler: @escaping ((Data) -> Void)) { // (K.1) self.completionHandler = completionHandler // (K.2) func postBody(params: [String: Int]) -> Data? { // (K.3) var paramsArr: [String] = [] for (key, value) in params { paramsArr.append("\(key)=\(value)") } let postBodyString: String = paramsArr.joined(separator: "&") let postBodyData: Data? = postBodyString.data(using: .utf8) return postBodyData } var request: URLRequest = URLRequest(url: url) // (K.4) request.httpMethod = "POST" request.httpBody = postBody(params: params) let session: URLSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) // (K.5) let task: URLSessionDownloadTask = session.downloadTask(with: request) task.resume() } 

(K.1)使用闭包,我们将在此函数之外工作,因此,我们使用@escaping表示它。
(K.2)我们将传递的闭包存储在completeHandler属性中。
(K.3)我们编写了一种方法,该方法将传递的参数的字典转换为字符串形式,然后以utf8编码将该字符串编码为Data类型。
(K.4)我们使用传递的url创建一个请求并进行配置:将POST设置为方法,并使用utf8作为请求正文来设置数据类型的参数。
(K.5)最后,创建一个具有默认配置和自己的委托的会话。 然后,我们创建一个任务以将该请求的结果加载到临时存储中。 最后,解冻任务。

现在让我们向MainViewController.swift添加必要的接口。 添加两个按钮以对votingUp和voteDown进行投票,这两个按钮将分别提高或降低当前笑话的等级。 我们还添加了“ Chuck Who?”按钮,该按钮将在Web视图部分中填充功能。

在MainViewController.swift中,插入初始化代码votUpButton,votDownButton和chuckWhoButton。

MainViewController.swift

 lazy var jokeLabel: UILabel! = … var jokeID: Int! lazy var activityView: UIActivityIndicatorView! = … //TODO:    lazy var stackView: UIStackView! = … 

按钮初始化代码
 lazy var voteUpButton: UIButton! = { // (L.1) let button: UIButton = UIButton(type: .system) // (L.2) button.setTitle("Vote Up", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteUp), for: .touchUpInside) // (L.3) self.view.addSubview(button) // (L.4) return button // (L.5) }() lazy var voteDownButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Vote Down", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.voteDown), for: .touchUpInside) self.view.addSubview(button) return button }() lazy var chuckWhoButton: UIButton! = { let button: UIButton = UIButton(type: .system) button.setTitle("Chuck Who?", for: .normal) button.sizeToFit() button.addTarget(self, action: #selector(self.chuckWho), for: .touchUpInside) self.view.addSubview(button) return button }() 

所有三个按钮的初始化代码都相同。

(L.1)我们声明一个惰性变量,并使用闭包对其进行初始化。
(L.2)在闭包内部,我们为按钮定义一个局部变量,一个系统类型,指定名称和大小。
(L.3)当按钮被按下时,我们将其与按钮相关联。
(L.4)添加到视图层次结构。
(L.5)然后,我们将此局部变量分配给我们的惰性变量。

接下来,在MainViewController.swift中,插入按钮的方法代码

MainViewController.swift

 class MainViewController: UIViewController { … func retrieveRandomJokes() { … } // TODO:    } // end of class MainViewController 

按钮的方法代码
 func voteUp() { let http: HTTPCommunication = HTTPCommunication() // (M.1) let params: [String: Int] = ["joke_id": self.jokeID, "vote": 1] // (M.2) if let url = URL(string: "http://example.com/rater/vote") { // (M.3) http.postURL(url, params: params) { (data) in // (M.4) print("\(data). Voted Up") } } } func voteDown() { let http: HTTPCommunication = HTTPCommunication() let params: [String: Int] = ["joke_id": self.jokeID, "vote": -1] if let url = URL(string: "http://example.com/rater/vote") { http.postURL(url, params: params) { (data) in print("\(data). Voted Down") } } } func chuckWho() { } 

上下表决方法的代码相同。

(M.1)创建用于与服务器通信的类对象。
(M.2)我们创建一个参数字典,其中包括我们要投票支持的笑话的ID,而投票的实际值为+1或-1。
(M.3)接下来,我们从培训域http://example.com/rater/vote.的地址栏中安全获取网址http://example.com/rater/vote.
(M.4)然后我们通过POST发送一个请求,以获取我们的参数url。 并在控制台上显示指示请求已完成的行。

接下来,在MainViewController.swift中,考虑对UI所做的更改,重写configStackView方法的代码。

MainViewController.swift

 func configStackView() -> UIStackView { let innerStackView: UIStackView = UIStackView(arrangedSubviews: [self.voteUpButton, self.voteDownButton]) // (N.1) innerStackView.axis = .horizontal // (N.2) innerStackView.distribution = .fillEqually let mainStackView: UIStackView = UIStackView(arrangedSubviews: [self.jokeLabel, innerStackView, self.chuckWhoButton]) // (N.3) mainStackView.spacing = 50 // (N.4) mainStackView.axis = .vertical mainStackView.distribution = .fillEqually self.view.addSubview(mainStackView) // (N.5) return mainStackView } 

(N.1)创建一个innerStackView,其中包含两个用于投票的按钮。
(N.2)我们暴露垂直轴; 分布是相同的。
(N.3)在主mainStackView中,从最后一部分向标签添加innerStackView和关于Chuck Norris的按钮。
(N.4)配置mainStackView。
(N.5)将mainStackView添加到视图层次结构。

我们启动我们的应用程序,然后看到如图所示的界面。 1个


图1我们的应用程序界面带有添加的投票按钮

我们验证在对一个笑话投票赞成或反对时,控制台中会显示以下消息:

345 bytes. Voted Up



345 bytes. Voted Down

表示已成功完成对服务器的POST请求。

我们使用icndb API和GET HTTP动词编写了一个玩笑应用程序。 我们能够在UIView上显示这些笑话,并且每个笑话都值得赞赏。 这些操作将POST请求发送到远程服务器,这应该保存我们的估计。

使用webViews显示网页


我们将添加一个webView来显示有关Chuck Norris的维基百科页面。 轻触按钮,她就会感到困惑。

在WebViewController.swift文件中,编写以下函数存根:

WebViewController.swift

 import UIKit class WebViewController: UIViewController { lazy var webView: UIWebView = { // (O.1) self.configWebView() }() lazy var toolbar: UIToolbar = { // (O.2) self.configToolbar() }() override func viewDidLoad() { super.viewDidLoad() } func back() { // (O.3) self.webView.goBack() } func forward() { self.webView.goForward() } } extension WebViewController { func configWebView() -> UIWebView { // (O.4) } func configToolbar() -> UIToolbar { // (O.5) } func configConstraints() { // (O.6) } } 

(O.1)我们为webView创建惰性变量
(O.2)和工具栏。
(O.3)我们创建向前和向后的方法,用于导航链接访问的历史记录。
(O.4)我们在扩展中添加了webView初始化方法
(O.5)和工具栏
(O.6)和接口的约束。

现在我们绘制每个函数的代码:

 func configWebView() -> UIWebView { let webView: UIWebView = UIWebView() // (P.1) self.view.addSubview(webView) return webView } 

(第1页)我们初始化webView并将视图添加到层​​次结构中。

 func configToolbar() -> UIToolbar { let toolbar: UIToolbar = UIToolbar(frame: CGRect.zero) // (Q.1) let backButton: UIBarButtonItem = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(self.back)) // (Q.2) let forwardButton: UIBarButtonItem = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(self.forward)) toolbar.setItems([backButton, forwardButton], animated: false) // (Q.3) toolbar.backgroundColor = UIColor.lightGray // (Q.4) self.view.addSubview(toolbar) return toolbar } 

(Q.1)创建一个零框架的工具栏。
(问题2)我们创建两个按钮来浏览访问链接的历史记录。 使用名称“ <”和“>”,并将动作附加到webView中的相应方法。
(第3季度)将这些按钮添加到工具栏。
(Q.4)设置背景颜色(如导航栏)为浅灰色。 并将视图添加到层​​次结构。

 func configConstraints() { self.webView.translatesAutoresizingMaskIntoConstraints = false // (R.1) NSLayoutConstraint.activate([ // (R.2) self.webView.topAnchor.constraint(equalTo: self.topLayoutGuide.topAnchor), self.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.webView.bottomAnchor.constraint(equalTo: self.toolbar.topAnchor) ]) self.toolbar.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ // (R.3) self.toolbar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.toolbar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.toolbar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), self.toolbar.heightAnchor.constraint(equalToConstant: 44.0) ]) } 

(R.1)禁用自动调整大小的蒙版,以免与我们的约束冲突
(R.2)对于webView,配置视图的所有四个侧面。
(R.3)对于工具栏,配置视图的三个侧面(顶部除外),但还要将高度设置为标准尺寸44.0。

并重写代码ViewDidLoad(),如下所示:

 override func viewDidLoad() { super.viewDidLoad() self.configConstraints() // (S.1) if let url = URL(string: "http://en.wikipedia.org/wiki/Chuck_Norris") { // (S.2) let request: URLRequest = URLRequest(url: url) // (S.3) self.webView.loadRequest(request) // (S.4) } } 

(S.1)调用约束的设置。
(S.2)从Wikipedia页面的地址栏中安全获取网址zh.wikipedia.org/wiki/Chuck_Norris
(S.3)我们从URL创建请求。
(S.4)然后我们在webView中执行给定的请求。

我们返回MainViewController并用调用有关Chuck Norris的信息的方法填充它。

MainViewController.swift

 func chuckWho() { self.navigationController?.show(WebViewController(), sender: self) // (T.1) } 

(T.1)当您在navigationController堆栈上单击此按钮时,请放置一个新的webView实例。

图 图2演示了webView在我们的应用程序中的外观。


2.我们已实现的webView的最终视图

Source: https://habr.com/ru/post/zh-CN415615/


All Articles